上傳文件的經典寫法:
<form id="uploadform" action="/Home/UploadFile" method="post" enctype="multipart/form-data"> <input type="file" name="uploadfile" /> <input type="submit" value="上傳" /> </form>
這里的表單form里只有1個file,所以也就只能一次上傳一個文件。如果需要上傳的文件很多,希望在打開瀏覽文件窗口后可以用鼠標框選或按着鍵盤的ctrl鍵用鼠標去點選,該怎么辦呢?
第一種方法——有幾個file就在form里寫幾個file:
<input type="file" name="uploadfile" /> <input type="file" name="uploadfile" /> <input type="file" name="uploadfile" /> <input type="file" name="uploadfile" /> <input type="file" name="uploadfile" /> ......
Oh my god !
如果有20個文件要上傳的話就寫20個<input type="file" name="uploadfile" />?不可以!
第二種方法——用jquery克隆文件
這樣form里還是只寫一個file,然后用jquey去動態添加。效果如圖:
<form id="uploadform" action="/Home/UploadFile" method="post" enctype="multipart/form-data"> 當前有<span id="inputNum">1</span>個文本框,已選擇<label id="fileNum">0</label>個文件。 <div id="upload"> <input type="file" name="uploadfile" style="border: 1px solid gray; width: 500px;" /> </div> <input type="submit" value="上傳" /> </form> <div id="status"></div>
我們想在文本框后面加一個“添加”、“刪除“的鏈接,點擊后增加一個文件框或刪除。
既然這個input file是動態添加的,那么后面跟着的“取消”、”增加”也是跟着一起創建的。本以為很難,結果用jquery反而很簡單的:
$("input[type=file]").after( //" <a href='#' class='clear-inputfile'>清除內容</a>" + " | " + " <a href='#' class='removeInputFile'>取消</a> " + " | " + " <a href='#' class='addInputFile'>增加</a>" );
先用input[type=file]找到這個inputy,然后在他的后面(after)加上超鏈接(a),就這么簡單!
Jquery:
//增加多個文本框 (復制當前行) $('.addInputFile').click(function () { var clone = $(this).parent().clone(true);//true:連事件一起復制 var file = clone.children("input[type=file]").val(null); $(clone).insertAfter($(this).parent()); });
clone() 方法
定義和用法
clone() 方法生成被選元素的副本,包含子節點、文本和屬性。
語法
$(selector).clone(includeEvents)
這里把事件一起復制了,所以是:
var clone = $(this).parent().clone(true);
注意我們復制的是<div id="upload">。
如果已經選擇了文件,克隆后會把文件框里的內容一起復制過來,所以在val()中加上null清除內容:
var file = clone.children("input[type=file]").val(null);
增加后也要有取消,當取消到只剩一個的時候給個提示。
//刪除 $('.removeInputFile').click(function () { var num = $('#upload input[type=file]').length; if (num == 1) { alert('必須保留一個!'); return false; } $(this.parentElement).remove(); });
動態添加文件操作完畢!
上傳文件后如果刷新瀏覽器會再次提交,這個問題很普遍,一不注意就重復提交了。所以這里采用ajax的方式上傳文件!用mvc的Ajax.BeginForm嗎?這個就是異步的啊!但是如果不配合一個插件來的話,單靠Ajax.BeginForm還是不能完成異步上傳文件並且避免刷新重復提交!!這個插件就是jquery.form.js。
$('form').ajaxForm({ dataType: "html",//json也可以,以避免FF里此錯誤:HierarchyRequestError: Node cannot be inserted at the specified point in the hierarchy ///判斷是否有文件 formData不支持IE! ///formData is an array of objects ///representing the name and value of each field that will be sent to the server; beforeSubmit: function (formData) { //沒有選擇文件則退出 if (selectedFileNum() == 0) { alert("請選擇文件!"); return false; } }, success: function (responseText) {//responseText:后台傳來的string status.html(responseText); }, error: function (xhr, textStatus, errorThrown) {//在前端擋住請求 alert('文檔上傳錯誤.' + xhr.responseText);//例如文件過大等錯誤 status.html(xhr.responseText); }, complete: function (xhr) { status.html(xhr.responseText); $("form").resetForm();//重置表單 } });//end jquey.form
Ajax.BeginForm也要配合jquery.form.plugin一起使用
Ajax.BeginForm的寫法:
@using (Ajax.BeginForm( "UploadFile", "Home", new AjaxOptions { UpdateTargetId = "status", HttpMethod = "Post" }, new { enctype = "multipart/form-data" } ) ) { <div id="upload"> <input type="file" name="uploadfile" style="border: 1px solid gray;" /> </div> <input type="submit" value="上傳" /> }
目前都是前端的操作,接下來看后台的保存文件到服務器是怎么做的。
Controller:
//上傳文件 [HttpGet] public ActionResult UploadFile() { return View(); } [HttpPost] public ActionResult UploadFile(IEnumerable<HttpPostedFileBase> uploadfile) { foreach (var file in uploadfile) { if (file != null && file.ContentLength > 0) { var fileName = Path.GetFileName(file.FileName); var path = Path.Combine(Server.MapPath("~/Uploads"), fileName); //如遇相同文件則先刪除再保存 if (System.IO.File.Exists(path)) System.IO.File.Delete(path); file.SaveAs(path); } } return Content("上傳完畢!"); }
因為上傳的是多個文件,所以這里要用IEnumerable<HttpPostedFileBase>類型。使用HttpPostedFileBase來接收傳遞的文件是ASP.NET MVC的推薦做法,不推薦用Request。要注意HttpPostedFileBase類型的參數uploadfile,這個名稱要和前端form里的input的name保持一致!
第三種方法——使用jquery上傳插件
上面說那么多,其實這個才是主角。
要想一次性在瀏覽窗口中選中多個文件同時上傳到服務器,還是要用個插件才方便。尋覓很久還是發現uploadify最合心意,其他的也很優秀,但是在IE瀏覽器里無法一次多選,只能是點一次選一個點一次選一個,然后一起上傳。
uploadify常用的一些設置:
$(document).ready(function () { $("#myUploadFile").uploadify({ //屬性 'auto': true, //自動上傳 'height': 30, //按鈕的高度 'width': 80, //按鈕的寬度 'swf': '../Scripts/Uploadify/uploadify.swf', //必輸入!flash.注意路徑!! 'fileObjName': 'uploadfile', //傳遞給后台程序的參數, 否則會接收不到! 'uploader': '../Home/Uploadify', //后台處理程序. 注意路徑!! 'queueID': 'queue', //顯示上傳隊列的容器 'buttonText': '選擇文件', //按鈕顯示文字 //'fileSizeLimit': '5MB', //'queueSizeLimit': 5, //同時允許上傳5個文件 'fileTypeExts': '*.xls;*.xlsx', 'removeCompleted': false, //上傳后保持隊列不消失 'requeueErrors': true, //事件 'onSelect': function (file)//從瀏覽窗口中選擇文件 { if (file.size / 1024 / 1024 > 10) //文件10M { alert('文件過大,請分批上傳!'); $('#myUploadFile').uploadify('cancel', file.id); //cancel方法可以帶上file的id作為參數,指定取消該項。 } }, 'onCancel': function (file)//從隊列中取消文件 { //alert('文件: ' + file.name + ' 被取消.'); //document.getElementById("fileCount").value -= 1; }, 'onClearQueue': function (queueItemCount) {//只在未上傳前有效,上傳后其實還保存在隊列中。 //alert(queueItemCount + ' file(s) were removed from the queue'); }, 'onDialogClose': function (queueData) //當瀏覽窗口關閉時 { //alert(queueData.filesQueued + ' files were queued of ' + queueData.filesSelected + ' selected files. There are ' + queueData.queueLength + ' total files in the queue.'); //document.getElementById("fileCount").value = queueData.queueLength; //隊列中文件數量 //document.getElementById("lblStatus").innerText = ""; }, 'onQueueComplete': function (queueData)//全部上傳完畢后觸發 { //alert(queueData.uploadsSuccessful + ' 個文件成功上傳。上傳后請點擊“提交”。'); //document.getElementById("fileCount").value = ""; //window.location.href = "Default.aspx"//重加載頁面 // window.location.reload(); // //alert("reload"); //alert("upload Done"); }, 'onUploadError': function (file, errorCode, errorMsg, errorString) { alert('The file ' + file.name + ' 上傳失敗: ' + errorString); //alert('errorCode:'+errorCode); //alert('errormsg:' + errorMsg);//500 //alert('errorString:' + errorString);//HTTP Error (500) }, 'onUploadSuccess': function (file, data, response) { //alert('The file ' + file.name + ' 上傳成功!'); //alert('data is :' + data);//data是controller傳來的str $('#msg').text(data); //alert('response is :' + response); //response is true }, 'onSelectError': function (file, errorCode, errorMsg) //錯誤信息 { switch (errorCode) { case -100: alert("上傳的文件數量已超過系統限制的" + $('#myUploadFile').uploadify('settings', 'queueSizeLimit') + "個文件"); break; case -110: alert("文件(" + file.name + ")大小超出系統限制的" + $('#myUploadFile').uploadify('settings', 'fileSizeLimit') + "大小!"); break; case -120: alert("文件(" + file.name + ")大小異常!"); break; case -130: alert("文件(" + file.name + ")類型不正確!"); break; } } }); }); <input type="file" id="myUploadFile" /> <p id="queue"></p>
Controller:
[HttpGet] public ActionResult Uploadify() { return View(); } [HttpPost] public ActionResult Uploadify(HttpPostedFileBase uploadfile) { if (uploadfile != null && uploadfile.ContentLength > 0) { var fileName = Path.GetFileName(uploadfile.FileName); var path = Path.Combine(Server.MapPath("~/Uploads"), fileName); if (System.IO.File.Exists(path)) System.IO.File.Delete(path); uploadfile.SaveAs(path); } return Content("上傳完畢!"); }
這樣就滿足了同時上傳文件的需要——在IE里。
--End--