拖拽上传图片
http://www.cnblogs.com/nh11/archive/2011/12/01/2267578.html
很久没有写过博客了,闲的时候没东西可写,忙的时候没有时间写。
前些天,后台的同事提建议说,上传图片不是很好用,后台在线编辑器用的是fckeditor。
这时候想到了很久前看过一遍提升用户体验:HTML5 拖放文件上传,于是就打算做一个拖拽上传图片的功能。
因为是后台用,所以不用考虑ie的兼容了。
1.拖图片进浏览器的时候阻止浏览器的默认行为(比如打开直接图片)
dropbox 给我们的容器添加上几个事件绑定dragenter,dragover,drop三个事件
dropbox.addEventListener("dragenter", function(e){ e.stopPropagation(); e.preventDefault(); }, false); dropbox.addEventListener("dragover" , function(e){ e.stopPropagation(); e.preventDefault(); }, false); dropbox.addEventListener("drop", function(e){ e.stopPropagation(); //个人的理解是,若在dropbox上的drop事件被监听多次,则两外的事件绑定无效了(不知道对不对) e.preventDefault(); //阻止默认动作 e.dataTransfer.files//一个file类型的数组,就是你拖拽进来的文件 }, false);
2.在得到一个文件file后,要把它显示出来
预览图片要用到FileReader
1 var rd=new FileReader(); 2 rd.onloadend=function(e){ 3 var img=document.createElement('img');//创建一个图片 4 img.src=this.result;//result就是读出来的内容 5 img.width=100; 6 img.title=file.name;//文件的原始名称 7 8 } 9 rd.readAsDataURL({file}); // 读取为dataurl
Filereader有下面几种方法,预览图片用到的是readAsDataURL。
方法名 | 参数 | 描述 |
---|---|---|
abort | none | 中断读取 |
readAsBinaryString | file | 将文件读取为二进制码 |
readAsDataURL | file | 将文件读取为 DataURL |
readAsText | file, [encoding] | 将文件读取为文本 |
3.把图片发送到服务端,进行处理
要提交到服务器,我们必须把图片读取为二进制的格式,这里就用到了Filereader的readAsBinaryString
1 var reader = new FileReader(); 2 reader.readAsBinaryString({file}); 3 4 reader.onloadend = function(){ 5 //bug(this.readyState); // 这个时候 应该是 2 6 //bug(this.result); //读取完成回调函数,数据保存在result中 7 var fileData=this.result; 8 var CRLF="\r\n"; 9 var xhr = new XMLHttpRequest(); 10 xhr.open('post', self.server, true); 11 //xhr.onreadystatechange=function(){}; 12 var boundary='------OTkwNzI0OTEx----';//一段随机字符串,最好根据时间来生成,为了分割多个表单项 13 // 模拟一个文件提交请求 14 xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary); 15 //xhr.setRequestHeader("Content-Length", file.size); 在chrome下会出错,可能是出于安全考虑,不允许这样做 16 var body = ''; 17 body += '--' + boundary + CRLF; 18 body += 'Content-Disposition: form-data; name="'+{fileFieldName}+'"; filename="' + file.name + '"'+CRLF; 19 body += "Content-Type: "+file.type+CRLF+CRLF; 20 body += fileData + CRLF; 21 body += "--" + boundary + "--"+CRLF; 22 xhr.onreadystatechange = function (aEvt) { 23 if (xhr.readyState == 4) { 24 if (xhr.status == 200) 25 alert(xhr.responseText); 26 else 27 console.log('Error', xhr.statusText); 28 } 29 }; 30 31 xhr.sendAsBinary(body); 32 33 34 35 }
上面代码的第18行filename直接用的原文件名称,并没有改名,这在原名为汉字的情况下会提示错误,应该处理一下,但不知道怎么弄
值 | 描述 |
---|---|
application/x-www-form-urlencoded | 在发送前编码所有字符(默认) |
multipart/form-data | 不对字符编码。 在使用包含文件上传控件的表单时,必须使用该值。 |
text/plain | 空格转换为 "+" 加号,但不对特殊字符编码。 |
上面是在w3cschool搜到的,既然multipart/form-data不对字符编码,但为什么会出错,希望知道的能告诉一声
模拟出来的数据要以二进制发送。火狐XMLHttpRequest 对象中有sendAsBinary()方法,这个是火狐私有的。chrome中没有,但是可以模拟sendAsBinary
1 XMLHttpRequest.prototype.sendAsBinary = function(datastr) { 2 function byteValue(x) { 3 return x.charCodeAt(0) & 0xff; 4 } 5 var ords = Array.prototype.map.call(datastr, byteValue); 6 var ui8a = new Uint8Array(ords); 7 this.send(ui8a.buffer); 8 }
服务端接收文件时和普通上传文件时一样
例如php代码:
if(!empty($_FILES)){ $file=$_FILES['new_image']; //new_image 就是上面的fileFieldName echo $file['name']; move_uploaded_file($file['tmp_name'],"./zf/".$file['name']); die(); }
4.显示上传进度
1 var xhr = new XMLHttpRequest(); 2 upload =xhr.upload; 3 upload.addEventListener("progress", updateProgress, false);//updateProgress 处理上传进度 4 xhr.open('post', “服务端url”, true); 5 6 function updateProgress(evt) { 7 if (evt.lengthComputable) { 8 var percentComplete = Math.round((evt.loaded * 100) / evt.total) 9 bug(percentComplete); 10 11 } 12 else { 13 // Unable to compute progress information since the total size is unknown 14 } 15 }
这个也挺简单,代码如上,不过要注意的是:
1.事件要绑定到 XMLHttpRequest的 upload上,这样才能监听上传进度。 XMLHttpRequest上同时也有progress事件,不过是监听下载的。
2. 火狐和chrome调用updateProgress频率好像一样,具体是每秒调用一次还是怎么着,我也不是很清楚。假如文件很小,而且网络很快(本地测试的,当然快)FF就不会触发progress事件。
在火狐下, 在上传完成时也不触发事件所以总是导致进度到不了100%,到了百分之80,90就不动了。
好了,功能基本都实现了,另外上面那些代码都是我在写篇文章是复制过来的(写了将近一个星期的时间,有空就写,程序基本功能完成是在两个星期前。)。
最后在附上一个我搜索资料过程中找到的一个jquery 插件 jquery-filedrop。