asp.net Web開發框架 (8) - 使用krajee進行非同步檔案上傳

這一篇介紹Bootstrap File Input這個套件,之所以會被歸類到asp.net Web開發框架系列,是因為整個套件和我們採用asp.net走SPA架構(不管是用WebForms或WebAPI)都非常的速配。

檔案上傳範例

首先我們有一個範例位於Github,如果你想要測試,下載這個範例後,基本上已經擁有所有你需要的檔案。請使用Visual Studio運行index.html這個檔案(對,沒錯,是pure html5),執行起來的畫面像是底下這樣:

這個套件精彩的地方是上傳預覽畫面,你會看到上圖中,它除了支援中文、多檔上傳、非同步上傳,還支援圖片預覽、Mp3檔案甚至可以撥放,會不會太誇張了點? 是的,人家就是支援…更不用說UI的部分很有誠意的原生支援正體中文(包含zh-TW的多國語言js)這也是我們使用此套件的原因之一。

如何使用

整個套件的使用可以完全採用AJAX方式(當然也支援傳統的submit/postback),搭配我們的SPA架構非常之合拍,也因此,你在運行該範例的時候,會發現只需要執行index.html即可(當然伺服器端接收檔案的部分還是需要Server Code,這我們後面介紹ReceieveFile.aspx的時候再說明)。

如果你看index.html,會發現程式碼如下:

<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<link href="Content/bootstrap.min.css" rel="stylesheet" />
<link href="Content/bootstrap-fileinput/css/fileinput.min.css" media="all" rel="stylesheet" type="text/css" />
<script src="Scripts/jquery-1.9.1.min.js"></script>
<!-- the main fileinput plugin file -->
<script src="Content/bootstrap-fileinput/fileinput.min.js"></script>
<!-- bootstrap.js below is needed if you wish to zoom and view file content
in a larger detailed modal dialog -->
<script src="Scripts/bootstrap.min.js"></script>
<!-- optionally if you need a theme like font awesome theme you can include
it as mentioned below -->
<script src="Content/bootstrap-fileinput/themes/fa/theme.js"></script>
<!-- optionally if you need translation for your language then include
locale file as mentioned below -->
<script src="Content/bootstrap-fileinput/locales/zh-TW.js"></script>
<script>
//上傳檔案完成後
function fileuploaded(event, data, id, index) {
//alert(index + ',' + data.files[index].name + ',' + data.response.URL);
$('#list').append('<li><a href="' + data.response.URL + '" target=_blank><span>' + data.files[index].name + '</span></a></li>');
}
//上傳前
function filepreupload() {
//清空list
$('#list').empty();
}
function setupFileUploadBox() {
//setup file upload
$("#input-id").fileinput(
{
language: 'zh-TW', //支援中文
showUpload: true,
previewFileType: 'any',
uploadAsync: true,
maxFileCount: 5,
uploadUrl: 'ReceieveFile.aspx'
}
).on('fileuploaded', fileuploaded).on('filepreupload', filepreupload);
}
</script>
</head>
<body>
<div class="row" style="margin: 12px">
<div class="col-lg-6">
<div class="panel panel-primary">
<div class="panel-heading">
範例 : 非同步上傳檔案
</div>
<div class="panel-body">
<input id="input-id" name="input-id[]" type="file" class="file" multiple data-show-preview="true" data-preview-file-type="text">
</div>
</div>
</div>
<div class="col-lg-6">
<h1>上傳檔案</h1>
<ol id="list"></ol>
</div>
</div>
<script>
//setup
setupFileUploadBox();
</script>
</body>
</html>
view raw index.html hosted with ❤ by GitHub
我們先看55行的部分,這是定義一個Input標記,其中的multiple意味著支援多檔上傳,data-show-preview="true"則是支援顯示預覽視窗。當然這些設定都可以在後面透過js來更動。

而66行javaScript所呼叫到32行的setupFileUploadBox(),就是進行相關的設定,其中language: 'zh-TW' 指定了多語UI採用中文(請留意這也是我們在19行引用了locales/zh-TW.js的原因),而後面幾個參數應該不需要太多解釋,showUpload是顯示上傳按鈕,uploadAsync說明了採用非同步上傳,maxFileCount指定了檔案上傳上限。

比較重要的是uploadUrl,這個參數指定了非同步檔案上傳的接收位置,由於這個範例採用WebForms,因此我們指定ReceieveFile.aspx,這部分我們待會後面介紹。(我覺得熟悉WebAPI的開發人員可以輕易的把ReceieveFile.aspx這隻改成WebAPI,因此我暫時就不動手改了)

先看上面程式碼43行的on後面定義的幾個事件hook:

fileuploaded事件發生在所有檔案上傳完成之後(嚴格說起來是伺服器端回應之後),而filepreupload事件則發生在所有檔案上傳之前,如果你還需要hook其他事件,可以參考該套件的官網說明(這套件的事件方法支援都算相當完整)

我們在fileuploaded這段程式碼當中,也只是把上傳完成之後,伺服器端傳來的檔案路徑(URL)給顯示出來而已…

伺服器端如何接收檔案,如何把特定訊息往前端送? 這個我們待會下面介紹,先看看該套件需要引用哪些css與js (我都整理在Content/bootstrap-fileinput資料夾下):
上圖是所有你需要引用的檔案,基本只有四個,如果你需要更多的功能(例如檔案排序…),則可能需要引用更多,其中1,3是與CSS相關,2是核心的js,4則是多國語言(中文需要的js)。

撰寫伺服器端接收檔案的C#程式碼

前端的code非常簡單,接著我們就要來看看後端接收檔案的程式碼如何撰寫。這個套件基本上走非常標準的http post把檔案往後端送,如果你採用非同步檔案上傳,則檔案是被一個一個往後端送的,也就是說,如果你上傳五個檔案,後端接收檔案的API會被呼叫五次。

後端接收檔案的API可以用WebAPI或是單純的apsx頁面撰寫,我們先看.aspx的版本:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebApplication8
{
/// <summary>
/// 回傳物件
/// </summary>
public class customResponse
{
/// <summary>
/// 我們只回傳一個URL
/// </summary>
public string URL { get; set; }
}
public partial class ReceieveFile : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//配合前端需要接收pure JSON response
Response.Clear();
Response.ContentType = "application/json";
//理論上,只會有1
if (Request.Files.Count > 0)
{
try
{
//我們把上傳的檔案放到temp資料夾底下
string path = Server.MapPath("temp\\");
for (int i = 0; i < Request.Files.Count; i++)
{
//為了上傳檔案不會重複,因此檔名一率加上guid
var guid = Guid.NewGuid();
var file = Request.Files[i];
path += guid + "-" + file.FileName;
//存檔
file.SaveAs(path);
//done
var JSON = Newtonsoft.Json.JsonConvert.SerializeObject(
new customResponse { URL = "temp\\" + guid + "-" + file.FileName });
//回應JSON
Response.Write(JSON);
}
}
catch (Exception ex)
{
//如果有錯誤,回應exception JSON
string jsonError = Newtonsoft.Json.JsonConvert.SerializeObject(ex);
Response.Write(jsonError);
}
//stop response
Response.End();
}
else
{
//如果別人亂入
string jsonError = Newtonsoft.Json.JsonConvert.SerializeObject(new Exception("No Any Files."));
Response.Write(jsonError);
Response.End();
}
}
}
}
你會發現這其實是一個很簡單的WebForm aspx,我們可以在PageLoad當中透過Request.Files接收到檔案,沒什麼特別的。這個範例刻意用aspx來撰寫,其實當然也可以用WebAPI,但在行為上其實都一樣。

由於多檔上傳時,前端的套件是把檔案一次一個往後端送,因此如果你同時上傳五個檔案,其實這個 ReceieveFile.aspx.cs 的Page_Load會被呼叫五次,因此,其實理論上 Request.Files.Count 在正常狀況下只會是1。

後面36-41行就是把取得的檔案儲存。然後43-46行回應一個很標準的JSON給前端。

這邊請特別留意,該套件要求,你撰寫的接收檔案的API(嚴格說起來不該稱之為API,應該叫做Backend Service),必須回應JSON,即便你啥也不想傳給前端,都必須回應一個空的{ }。

你會看到我們new了一個13-19行定義的customResponse物件,其實裡面也只有一個URL屬性,你可以自己加上其他的屬性,如果需要的話。不過最少,也應該回應一個檔案上傳到後端之後的id或URL,範例中我們選擇回應URL,因此你會看到我們在44行組出被上傳檔案的URL,然後在46行回傳給前端。

就這樣,其他的code全是例外處理。

整個範例的source code在: https://github.com/isdaviddong/KrajeeFileUploadExample
該套件的官網: http://plugins.krajee.com/file-input

整個套件採BSD 3-Clause License,這年頭好心人真多…
--------------------------------------------
如果需要即時取得更多相關訊息,可按這裡加入FB專頁。若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。

留言

這個網誌中的熱門文章

使用LM Studio輕鬆在本地端以API呼叫大語言模型(LLM)

實際嘗試使用DeepSeek API

使用 Dify 以No Code方式建立記帳機器人

使用 Dify API 快速建立一個包含前後文記憶的對談機器人

使用 Dify 建立企業請假機器人