先說一些廢話。這兩天在爲ASP.NET MVC程序添加上傳附件功能。HTML默認的input file控件不但難看,而且需要結合form實現提交(雖然可以使用H5的FormData對象來異步提交,但IE不太支持)。花了兩天時間研究這個功能,期間找到了uploadify,Plupload以及zui的上傳附件插件,uploadify和Plupload配置有些麻煩,而zui雖然樣式很好,而且配置簡單,但由於基於bootstrap做了定製,我的程序也用到了bootstrap,引用zui的上傳控件後導致css衝突,頁面變亂,因此不得不放棄(自己的程序基本上完成,不願再調整頁面佈局)。
本來打算就使用form異步提交吧,但還有些不甘心,就重新組織關鍵詞進行百度,發現一個新的插件jQuery-File-Upload,但可惜的是,雖然官方文檔很詳細,插件功能很強大,但示例基本上都是自動上傳的,而通過外部按鈕上傳的示例在大量的文檔內容中被我給忽略了,導致多花費了半天時間。
經過不斷摸索,和網友的文章,終於找到一個合適的方法實現所需功能:點擊外部按鈕實現異步上傳文件至服務器。其實操作很簡單,在fileupload()函數的add選項中爲外部按鈕綁定點擊事件並調用data.submit()函數,具體請見下方js代碼$(’#fileUpload’).fileupload()部分。
使用這個插件前先按照網友的文章對文件上傳按鈕進行了美化,這裏一併寫出來做個記錄。
對上傳文件控件進行美化其實也很簡單,就是將控件本身變透明,然後用其他標籤將其包圍,並改爲自己喜歡的樣式。
顯示效果。
附上代碼。
上傳文件的html頁面組織(使用bootstrap和網友提供的樣式),超鏈接a標籤包圍input[file]控件,並添加刪除按鈕,在file控件選擇文件後調用updateFileMessage函數更新頁面提示,點擊執行導入按鈕進行文件上傳:
@* 上傳文件開始 *@
<a href="javascript:;" class="a-upload form-control" id="fileTip" title="尚未選擇任何文件">
<input type="file" id="fileUpload" name="files[]" multiple data-url="@Url.Action("ImportData")"
onchange="updateFileMessage('fileUpload', 'fileCount', 'fileTip', '尚未選擇任何文件');"
accept="application/msexcel,application/msword,application/pdf,image/x-png,text/plain,
application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
<span>選擇要導入的數據文件</span>[<span id="fileCount">0</span>]
<span style="width:auto;height:auto;" title="點擊清除所選文件" onclick="clearFile('fileUpload','fileCount', 'fileTip', '尚未選擇任何文件');"
class="close">x</span>
</a>
<input type="button" class="btn btn-sm btn-primary" id="btnImport" value="執行導入" />
@* 上傳文件結束 *@
@* 以下爲upload插件所需的基本js文件,我的jquery引用在佈局頁,這裏沒有顯示,單獨使用時需要引用jquery文件 *@
<script src="~/Scripts/jquery.ui.widget.js"></script>
<script src="~/Scripts/jquery.iframe-transport.js"></script>
<script src="~/Scripts/jquery.fileupload.js"></script>
自定義的js代碼,用於選擇文件後現實提示內容,包括顯示所選文件數量,並在title中提示文件名稱(因爲chrome和ie等瀏覽器顯示input[file]的樣式不一樣,只好統一一下):
//felem爲上傳文件控件id, fcount爲顯示文件數量的標籤id,ftip爲顯示title提示的標籤id, defaultTip爲默認的提示信息
function updateFileMessage(felem, fcount, ftip, defaultTip) {
//使用$("#id")方式找不到files屬性(選擇的文件列表),使用getElementById可以,後來發現使用jquery可以實現,這裏暫時不改了
var files = document.getElementById(felem).files;
$("#" + fcount).html(files.length);
var info = "";
for (var i = 0; i < files.length; i++) {
info += files[i].name + '[大小:' + calSize(files[i].size) + "]" + "\n";
}
if (info == "") {
info = defaultTip == null ? "未選擇任何文件" : defaultTip;
}
$("#" + ftip).attr("title", info);
//將文件大小由字節換算爲KB或MB顯示
function calSize(size) {
size /= 1024;
if (size < 1024) {
return size.toFixed("1") + "KB";
}
return (size / 1024).toFixed("1") + "MB";
}
}
//清除所選文件並更新提示,我的清理方法可能有問題,還需要確認文件是否被清除
function clearFile(felem, fcount, ftip, defaultTip) {
document.getElementById(felem).files = null;
$("#" + fcount).html(0);
$("#" + ftip).attr("title", defaultTip);
}
//使用jquery-fileupload控件控制上傳文件
$('#fileUpload').fileupload(
{
dataType: "json",
add: function (e, data) {
//敲黑板!官網是動態添加按鈕,這裏使用已定義的按鈕來提交。
//這裏還有待確認,按照官網介紹,add選項是每添加一個文件調用一下回調函數,所以對於一次選擇多個文件會不會重複執行,我還沒有確認。
$("#btnImport").click(function () {
data.submit();
});
},
done: function (e, data) {
alert(data);
},
fail: function (e, data) {
alert(data);
}
});
上傳文件樣式(引用自網友haorooms的文章):
/*a upload */
.a-upload {
padding: 4px 10px;
/*height: 20px;*/
line-height: 25px;
position: relative;
cursor: pointer;
color: #888;
background: #fafafa;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
display: inline-block;
*display: inline;
*zoom: 1
}
.a-upload input {
position: absolute;
font-size: 100px;
right: 0;
top: 0;
opacity: 0;
filter: alpha(opacity=0);
cursor: pointer
}
.a-upload:hover {
color: #444;
background: #eee;
border-color: #ccc;
text-decoration: none
}
好了,就寫這些吧,因爲記性有些差,趁着找到解決方法趕緊記錄下來,以免後面給忘記。寫的內容有些凌亂,而且有部分內容我還沒有確認是否真的可行,但腳印需要一步一步踩出來,慢慢探索,多思考終究會找到解決方法。
後續補充:
如果需要一次上傳多個文件,並且希望只發送一次請求,那麼需要將singleFileUploads選項設置爲false。
如果需要限制一次上傳文件的數量,可以通過設置limitMultiFileUploads選項的數字來實現,例如設置爲5,但請注意,這個選項只是限制一次請求發送文件的數量,如果所選文件數量超過設置值,那麼會分多次發送,例如選擇了7個文件,第一次會發送5個,第二次發送2個。真正實現文件限制數量需要設置send選項,在send選項的函數中進行檢驗,如果超過數量則返回false以終止發送。需要注意的是,send返回false後插件會調用fail選型綁定的函數,可以在這個函數裏做一些處理。還需要注意的是:如果設置了limitMultiFileUploads,那麼會因爲分批發送文件導致每次send的數量不會超過你在send函數中檢驗的數量,所以如果要在send中檢查,最好不要設置limitMultiFileUploads。
因爲我的上傳控件美化是在使用這個插件之前做的(當時未發現這個插件),所以修改控件的樣式函數綁定的位置是自己研究的,用起來比較亂。而使用這個插件時,插件提供了需要綁定函數的選項,例如change, add等,我們可以按需要將樣式修改函數綁定到這些事件上,這樣不但思路清楚,而且還好維護。
另外,在ASP.NET MVC Action中接收文件時,我發現使用foreach(HttpPostedFileBase file in Request.Files)無法獲取到文件,需要通過普通的循環使用HttpPostedFileBase file = Request.Files[i]來獲取,這個問題還沒有詳細研究,有時間再說。
補充代碼。
調整後的fileupload js代碼:
$('#fileUpload').fileupload(
{
dataType: "json",
singleFileUploads: false,
//limitMultiFileUploads:5,
add: function (e, data) {
$("#btnImport").click(function () {
data.submit();
});
},
send: function (e, data) {
if (data.files.length > 5) {
alert("文件數量不能超過5個!");
return false;
}
},
done: function (e, data) {
// data.result
// data.textStatus;
// data.jqXHR;
alert(data.result.message);
},
fail: function (e, data) {
// data.errorThrown
// data.textStatus;
// data.jqXHR;
if (data.errorThrown != undefined) {
alert(data.errorThrown);
}
}
});
ASP.NET MVC Action中的代碼:
[HttpPost]
public JsonResult ImportData()
{
if (Request.Files.Count == 0 || Request.Files.Count > 5)
{
return Json(new { success = "false", message = "導入數據失敗!文件數量不能0,也不能超過5個。" });
}
var dir = new DirectoryInfo(Path.Combine(Server.MapPath("~"), "Attachment", "ImportData"));
try
{
if (!dir.Exists)
dir.Create();
//fileupload默認每次只提交一個文件,同時選擇多個會分多次提交,這裏需要考慮怎樣處理
//需要修改singleFileUpload選項爲false實現一次請求發送多個文件
//使用foreach無法實現遍歷文件,嘗試改用for循環
for (int i=0;i<Request.Files.Count;i++)
{
var file = Request.Files[i];
var fileSavePath = Path.Combine(dir.FullName, $"{file.FileName.Substring(0, file.FileName.LastIndexOf('.'))}_{DateTime.Now.ToString("yyyyMMddHHmmss")}{Path.GetExtension(file.FileName)}");
file.SaveAs(fileSavePath);
}
return Json(new { success = "true", message = "已導入!" });
}
catch (Exception ex)
{
LogService.Current.Error("導入數據失敗!", UserContext.GetCurrentUserAccount(), ex);
return Json(new { success = "false", message = $"導入數據失敗!錯誤:{ex.Message}" });
}
}
參考文章:
jQuery-File-Upload 使用,jQuery-File-Upload上傳插件
ASP.NET MVC中使用Jquery File Upload插件上傳文件
插件官網介紹:How to start uploads with a button click(官網頁面最下方)
[css inputtype=file] 樣式美化,input上傳按鈕美化