上传文件的经典写法:
<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--