jQuery-File-Upload通過點擊外部按鈕上傳附件,並美化Html文件上傳控件

先說一些廢話。這兩天在爲ASP.NET MVC程序添加上傳附件功能。HTML默認的input file控件不但難看,而且需要結合form實現提交(雖然可以使用H5的FormData對象來異步提交,但IE不太支持)。花了兩天時間研究這個功能,期間找到了uploadifyPlupload以及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上傳按鈕美化

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章