再说说文件上传客户端的问题。阮一峰大神这篇解释挺清楚文件上传的渐进式增强 。所以原理就不再赘述,就在这记录下实现的方法。
form表单上传是最简单的文件上传,只需要一个form标签,把enctype设为multipart/form-data。action设置为上传路径。缺点是提交后会跳转。基本不会再用。
1 2 3 4 <form enctype ="multipart/form-data" method ="POST" > <input id ="file1" type ="file" name ="file1" > ... </form >
把form表单的action值指向一个同页面隐藏的iframe。此方法页面不会跳转,也不会阻塞页面(传统form上传是同步上传),甚至可以获取到服务器的返回信息。
ajax上传 ajax上传是现在的主流方法,但是只能兼容IE10以上的高级浏览器。IE8呵呵
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 var formData = new FormData();formData.append('name1' , $('#upload' ).files[0 ]) formData.append('name2' , $('#upload' ).files[1 ]) formData.append('name3' , $("#text" ).val()) $.ajax({ url: url, method: "POST" , data: formData, processData: false , contentType: false , xhr: function (e ) { var myXhr = $.ajaxSettings.xhr(); console .log(myXhr) if (myXhr.upload) { myXhr.upload.onprogress = function (e ) { if (e.lengthComputable) { progress(e.loaded, e.total, myXhr) } } } return myXhr }, success: function (res ) { console .log(res) } });
有几点需要注意的。
文件上传需要用到FormData对象来包装文件,模拟表单提交
1 2 3 4 5 var formData = new FormData();formData.append('name1' , $('#upload' ).files[0 ]) formData.append('name2' , $('#upload' ).files[1 ]) formData.append('name3' , $("#text" ).val())
用append
方法给formData
添加数据。
dom.files[index]
方法来获取input标签内的文件数据。
console.log(dom.files[index])也许打印出来的内容看起来是个普通的对象,好像并没有包含文件内容。但是事实上这样的确可以把文件上传上去。
用jQuery上传。有两个参数是必须的: processData: false
和contentType: false;
processData设置为false。因为data值是FormData对象,不需要对数据做处理。默认情况下,通过data选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),都会处理转化成一个查询字符串,以配合默认内容类型 “application/x-www-form-urlencoded”。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。
contentType设置为false。因为是由form表单构造的FormData对象,且已经声明了属性enctype=”multipart/form-data”,所以这里设置为false。发送信息至服务器时内容编码类型。默认值是”application/x-www-form-urlencoded; charset=UTF-8”,适合大多数情况。
cache设置为false,上传文件不需要缓存。
ajax进度条 一切要说的话都在代码里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 xhr: function ( ) { var myXhr = $.ajaxSettings.xhr(); if (myXhr.upload) { myXhr.upload.onprogress = function (ev ) { if (ev.lengthComputable) { progress(ev.loaded, ev.total) } } } return myXhr }, success: function (res ) { console .log(res) }
原生的方法,需要在new一个xhr对象和open、send之间给xhr.upload绑定progress事件即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function upload ( ) { var xhr = new XMLHttpRequest(); var fd = new FormData(); fd.append("fileName" , file); xhr.upload.addEventListener("progress" , uploadProgress, false ); xhr.open("POST" , "../UploadServlet" ,true ); xhr.send(fd); } function uploadProgress (evt ) { if (evt.lengthComputable) { progress(ev.loaded, ev.total) } }
拖拽方法封装 拖拽方法相关的事件有 dragenter、dragleave、dragover、drop。一般情况下都要配合起来用才能完成一次完整的退拽操作
需要说明的有几点:
1、 可以读取到文件路径和文件内容的事件对象在drop事件下。 2、 如果在绑定拖拽方法的对象下还有子元素,鼠标进入该子元素范围内也会触发dragleave、dragenter、dragover等事件。所以会导致一些奇怪的事情,比如提前移除高亮样式等。解决办法是添加一个计数器。当dragleave次数等于dragenter次数就可以触发真正的dragleave回调。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 function initDrag ( selector, callback ) { let $elem = $(selector); $elem.counter = 0 ; $(selector).on('dragenter' , function (e ) { e.preventDefault(); e.stopPropagation(); $elem.counter++; $elem.addClass( 'highLight' ) }) $(selector).on('dragleave' , function (e ) { e.preventDefault(); e.stopPropagation(); $elem.counter--; if ($elem.counter === 0 ) { $elem.removeClass( 'highLight' ) } }) $(selector).on('dragover' , function (e ) { e.preventDefault(); e.stopPropagation(); }) $(selector).on('drop' , function (e ) { e.preventDefault(); e.stopPropagation(); $elem.counter--; if ($elem.counter === 0 ) { $elem.removeClass( 'highLight' ) } if ($.isFunction(callback)) callback(e); }) }