拖拽上传图片

 

很久没有写过博客了,闲的时候没东西可写,忙的时候没有时间写。

前些天,后台的同事提建议说,上传图片不是很好用,后台在线编辑器用的是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

源码下载

posted on 2011-12-01 18:11  倪浩  阅读(11667)  评论(20编辑  收藏  举报

导航