html5拖拽图片批量ajax无刷新进度上传
1、前端拖拽图片
之前有篇文章说到HTML5的拖拽(drag
、drop
,详见:/post/jquery-plugin-1-jquery-drag-and-html5-draggable-api-and-compatibility.html)。这里说的拖拽图片只是弱化了拖的概念,而强化了拽的操作。从浏览器外部拖动一个文件到浏览器中来,如:
拖动文件到浏览器之后,就会打开浏览器支持的文件,如常用的txt、图片等,如下:
操作如上常用文件,一般都有默认行为来处理这样的事件。这里说的拖拽上传也是一个道理,就是要做的是从浏览器外部拖动文件到浏览器中来,并且在当前页面上传该文件。在表面上看来,这个方法很适合用户体验,但实际情况是用户体验急剧下降。因为大多数人浏览网页的时候是全最大化窗口查看的,而如果要拖拽上传文件的话,那么就需要把浏览器窗口后,非常的麻烦。就比如wordpress的添加媒体功能,也是支持拖拽上传图片的,可用过的人又有多少呢?
倒还不如点击选择图片操作来的更加快捷,不过本文旨在说明问题。也许有更好玩的例子没有被发现而已,在此只做抛砖引玉。
1.1、拖拽文件的files对象
在拖拽文件到浏览器,我们需要处理这些文件。
$.fn.listen=function(type,fn){returnthis.each(function(){$(this)[0].addEventListener(type,function(e){if(!fn.call($(this),e)){e.stopPropagation();e.preventDefault();}},0);}); } $(document).bind("dragenter",function(){returnfalse; }).bind("dragover",function(){returnfalse; }).listen("drop",function(e){// 这里处理拖拽进来的文件(们) });
如上,可能细心的朋友发现了一些不同。那就是document使用了两个方法来绑定事件,dragenter
、dragover
使用jquery的bind
方法绑定,而drop事件却使用自定义的listen
方法绑定,这是为什么呢?因为jquery的事件绑定,为了兼容操作处理的事件的参数event,删减了一些属性使其在各个浏览器都保持一致。而此处用的是event的HTML5属性,恰巧是不兼容低级浏览器的,所以使用jquery的bind
方法是无法获取的。我们可以来验证一下这个说法:
$(document).bind("dragenter",function(){returnfalse; }).bind("dragover",function(){returnfalse; }).bind("drop",function(e){console.log(e); }).listen("drop",function(e){console.log(e); });
拖拽文件到这个页面,在浏览器控制台得到两个不尽相同的对象:
如图示黄色背景的dataTransfer
是浏览器默认的event所独有的,而我们此处用的files
对象这是在这里面。
console.log(e.dataTransfer.files);
在浏览器控制台输出了:
由图示可知,该files对象是一个数组。值得注意的是,该files对象只读。可以接收到文件的基本信息(最后修改时间、文件名称、大小、类型),我们就可以在客户端来处理上传文件之前的操作。比如限制文件必须为图片格式、文件的大小不能超过100KB。
$(document).bind("dragenter",function(){returnfalse; }).bind("dragover",function(){returnfalse; }).listen("drop",function(e){var files=e.dataTransfer.files;var error='';for(var i=0;i<files.length;i++){console.log(files[i]);if(files[i].size>1024*100)error+='第'+(i+1)+'个文件超过大小限制\n';if(!/image/i.test(files[i].type))error+='第'+(i+1)+'个文件不是图片格式\n'; } if(error)alert(error); });
会弹出如下警告:
1.2、处理文件的FileReader对象
用户拖拽了符合我们预期的文件,我们就需要对其进行操作。比如本文要做的是用户必须上传100KB以内的图片类型文件。在获取到files对象之后,我们要使用FileReader
对象来读取file的文件(该对象人如其名)。
$(document).bind("dragenter",function(){returnfalse; }).bind("dragover",function(){returnfalse; }).listen("drop",function(e){var files=e.dataTransfer.files;var error='';for(var i=0;i<files.length;i++){if(files[i].size>1024*100)error+='第'+(i+1)+'个文件超过大小限制\n';if(!/image/i.test(files[i].type))error+='第'+(i+1)+'个文件不是图片格式\n'; } // 有错误抛弃 if(error){alert(error);return; } // 处理文件队列 for(var i=0;i<files.length;i++){read(files[i]); } }); function read(file){var reader=newFileReader();reader.οnlοad=function(e){$("body").append('<img src="'+e.target.result+'" alt="" />');}reader.readAsDataURL(file); }
在拖拽合适大小的图片文件到该页面之后:
FileReader
对象是干嘛的呢?它可以读取文件,并进行相应的操作,涉及到安全问题,该操作不会对图片的源文件产生影响。该对象有如下属性和方法以及事件:
- 属性:为空
- 方法:
- about:中断文件读取
- readAsBinaryString:读取文件为二进制
- readAsDataURL:读取文件dataUrl(本例用到的)
- readAsText:读取文件为文本
- 事件:
- onabort:读取文件中断时触发
- onerror:读取文件出错时触发
- onload:读取文件成功时触发(本例用到的)
- onloadstart:读取文件开始时触发
- onprogress:读取文件中时一直触发
- onloadend:读取文件结束时触发(成功和失败都会触发,如
jquery.ajax
的complete
)
在监听到文件读取成功时,该事件参数e有e.target.result
指向的是读取的结果(即本例描述的文件的二进制url)。
1.3、处理表单的FormData对象
选到了合适大小的图片文件,我们就需要把这些文件无刷新上传到服务器,但我们如何发送这些图片到后端呢?传统的ajax方法一直是发送文本格式(Content-Type:application/x-www-form-urlencoded)而发送图片这些大文件需要用到浏览器的原生表单(Content-Type:multipart/form-data)。所幸的是,HTML5的支持的FromData
属性可以帮忙完成这一个过程,即封装图片成表单数据以用于提交到后端。
FormData
类似于jquery.serialize
,jquery这一次又走在了前列。不过FormData
比serialize
要强大一点,它不仅可以在提交表单数据里增加纯文本也可以增加图片等二进制数据。其基本使用方法是:
- var form1=newFormData();
- form1.append("name1","value1");
- form1.append("name2","value2");
- form1.append("file",file对象);
如上,其作用相当于:
- <form>
- <inputtype="text"name="name1"value="value1">
- <inputtype="text"name="name2"value="value2">
- <inputtype="file"name="file"id="">
- </form>
所以接着1.2继续,把合适的图片组装成form数据便于异步传输。
- $(document).bind("dragenter",function(){
- returnfalse;
- }).bind("dragover",function(){
- returnfalse;
- }).listen("drop",function(e){
- var files=e.dataTransfer.files;
- var error='';
- for(var i=0;i<files.length;i++){
- if(files[i].size>1024*100)error+='第'+(i+1)+'个文件超过大小限制\n';
- if(!/image/i.test(files[i].type))error+='第'+(i+1)+'个文件不是图片格式\n';
- }
- // 有错误抛弃
- if(error){
- alert(error);
- return;
- }
- // 处理文件队列
- for(var i=0;i<files.length;i++){
- read(files[i]);
- }
- // 组装成表单数据
- var formData=newFormData();
- for(var i=0;i<files.length;i++){
- formData.append('files[]',files[i]);
- }
- });
- // 读取图片
- function read(file){
- var reader=newFileReader();
- reader.onload=function(e){
- $("body").append('<img src="'+e.target.result+'" alt="" />');
- }
- reader.readAsDataURL(file);
- }
注:以上代码仅供示例,上述的formData.append('files[]',...)
,有中括号表示多文件批量上传。
1.4、ajax上传的XMLHttpRequest对象
有了表单数据,我们就可以使用异步传输发送给服务端。
- $(document).bind("dragenter",function(){
- returnfalse;
- }).bind("dragover",function(){
- returnfalse;
- }).listen("drop",function(e){
- var files=e.dataTransfer.files;
- var error='';
- for(var i=0;i<files.length;i++){
- if(files[i].size>1024*100)error+='第'+(i+1)+'个文件超过大小限制\n';
- if(!/image/i.test(files[i].type))error+='第'+(i+1)+'个文件不是图片格式\n';
- }
- // 有错误抛弃
- if(error){
- alert(error);
- return;
- }
- // 处理文件队列
- for(var i=0;i<files.length;i++){
- read(files[i]);
- }
- // 组装成表单数据
- var formData=newFormData();
- for(var i=0;i<files.length;i++){
- formData.append('files[]',files[i]);
- }
- // 异步传输
- ajax();
- });
- // 读取图片
- function read(file){
- var reader=newFileReader();
- reader.onload=function(e){
- $("body").append('<img src="'+e.target.result+'" alt="" />');
- }
- reader.readAsDataURL(file);
- }
- // 异步传输
- function ajax(formData){
- var xhr=newXMLHttpRequest();
- xhr.open("post","upload.php");
- xhr.onload=function(){
- alert("上传完成!");
- }
- xhr.send(formData);
- }
如果上传不出错的话,那么在上传结束的时候会弹出“上传完成!”的确认框。
1.5、ajax上传的进度
因为上传文件和上传纯文本,其数据量不是一个级别的,所以为了友好的用户体验,我们需要实时告诉用户上传的进度目前是多少。可喜的是,HTML5已经帮我们完成这个步骤,可以这么做来实时获取当前上传的进度:
- // 异步传输
- function ajax(formData){
- var xhr=newXMLHttpRequest();
- xhr.open("post","upload.php");
- xhr.onload=function(){
- alert("上传完成!");
- }
- // 上传进度
- xhr.upload.onprogress=function(e){
- if(e.lengthComputable){
- var percent =(e.loaded / e.total *100|0)+"%";
- console.log(percent);
- }
- }
- xhr.send(formData);
- }
关于XMLHttpRequest
的更多使用方法,这里不做赘述,可以参考文章末尾的参考资料。
1.6、完整的源代码
- <!doctype html>
- <htmllang="en">
- <head>
- <metacharset="UTF-8">
- <title>拖拽图片到这里来并上传</title>
- <style>
- h2 small{
- color:#888;
- font-weight: normal;
- margin-left:30px;
- font-size:80%;
- }
- .box{
- border:1px solid #ccc;
- background:#f5f5f5;
- padding:10px;
- margin-bottom:20px;
- }
- .box2{
- border:1px solid #EB9D45;
- background:#FFF2E3;
- padding:10px;
- margin-bottom:20px;
- }
- #progress{
- margin-left:10px;
- }
- .box img,
- .box2 img{
- max-height:100px;
- height:auto;
- width:auto;
- padding:2px;
- border:1px solid #ddd;
- background:#fff;
- margin-right:20px;
- margin-bottom:20px;
- }
- </style>
- </head>
- <body>
- <divclass="box">
- <h2>拖拽图片到这里来<smallid="message"></small></h2>
- <divid="box"></div>
- </div>
- <p>原文:<ahref="/post/undefined.html700">/post/undefined.html700</a></p>
- <divclass="box2">
- <h2>已上传的图片<smallid="progress"></small></h2>
- <divid="box2"></div>
- </div>
- <scriptsrc="http://libs.baidu.com/jquery/2.0.3/jquery.min.js"></script>
- <script>
- $.fn.listen=function(type,fn){
- returnthis.each(function(){
- $(this)[0].addEventListener(type,function(e){
- if(!fn.call($(this),e)){
- e.stopPropagation();
- e.preventDefault();
- }
- },0);
- });
- }
- var $box=$("#box");
- var $message=$("#message");
- var $box2=$("#box2");
- var $progress=$("#progress");
- var isuploading=0;
- var hasBtSize=0;
- var errorArray=[];
- $(document).bind("dragenter",function(){
- returnfalse;
- }).bind("dragover",function(){
- returnfalse;
- }).listen("drop",function(e){
- if(isuploading)return;
- var error='';
- var num=1;
- var files=e.dataTransfer.files;
- var j=files.length;
- $box.empty();
- for(var i =0; i < j; i++){
- (function(i){
- var reader =newFileReader();
- reader.onload =function(event){
- $box.append("<img src='"+ event.target.result +"' /> ");
- if(files[i].size>1024*400||!/image/.test(files[i].type)){
- errorArray.push(num);
- }
- num++;
- if(num>j){
- if(errorArray.length){
- error=errorArray.join(',');
- $message.html("其中第 "+error+" 个文件大小超过限制或不是图片,本次上传已被取消。");
- errorArray=[];
- return;
- }
- upload(files);
- }
- };
- reader.readAsDataURL(files[i]);
- })(i);
- }
- returnfalse;
- });
- function upload(files){
- if(isuploading)return;
- isuploading=1;
- var formData=newFormData();
- var isComplete=0;
- for(var i=0; i <files.length; i++){
- formData.append("files[]",files[i]);
- };
- var xhr=newXMLHttpRequest();
- xhr.open('post','upload.php');
- xhr.onload=function(){
- $progress.html("上传完成!");
- };
- xhr.onreadystatechange=function(){
- if(xhr && xhr.readyState ===4){
- status = xhr.status;
- if(!isComplete && status >=200&& status <300|| status ===304){
- isComplete =true;
- var json=$.parseJSON(xhr.responseText);
- $box2.empty();
- $box.empty();
- $message.empty();
- $.each(json,function(key,val){
- $box2.append("<img src='"+ val +"' />");
- });
- isuploading=0;
- }elseif(!isComplete){
- isComplete =true;
- $box.html("网络错误!");
- isuploading=0;
- }
- xhr =null;
- }
- }
- xhr.upload.onprogress =function(event){
- if(event.lengthComputable){
- var percent =(event.loaded / event.total *100|0)+"%";
- $progress.html(percent);
- }
- }
- xhr.send(formData);
- }
- </script>
- </body>
- </html>
在上传过程中:
2、后台上传处理
- <?php
- $uploaddir ='img/';
- $src_array=array();
- if(isset($_FILES['files'])){
- foreach($_FILES['files']["error"]as $key => $error){
- if($error==UPLOAD_ERR_OK){
- if(!preg_match("#image#",$_FILES["files"]["type"][$key]))continue;
- if($_FILES["files"]["size"][$key]>1024*400)continue;
- $tmp_name = $_FILES["files"]["tmp_name"][$key];
- $name = $_FILES["files"]["name"][$key];
- $name= date("YmdHis",time()).preg_replace("#[^\w\.]#","",$name);
- $uploadfile = $uploaddir.$name;
- $ret=move_uploaded_file($tmp_name, $uploadfile);
- if($ret){
- $src_array[]=$uploadfile;
- }
- }
- }
- }
- echo json_encode($src_array);
3、demo
demo地址:http://demo.qianduanblog.com/2700/1.html
jquery批量上传插件详细见:/post/jquery-plugin-11-jquery-upload-free-refresh-batch-ajax-progress-upload.html。
4、参考资料
- http://www.zhangxinxu.com/wordpress/2013/10/understand-domstring-document-formdata-blob-file-arraybuffer/
- http://www.jsmix.com/blog/html5/file-reader.html
- https://developer.mozilla.org/en-US/docs/Web/API/FormData?redirectlocale=en-US&redirectslug=Web%2FAPI%2FXMLHttpRequest%2FFormData
- http://www.html5rocks.com/zh/tutorials/file/xhr2/
- https://developer.mozilla.org/en-US/docs/Web/API/XMLHTTPRequest
- http://developer.51cto.com/art/200911/164751.htm
- http://www.html5rocks.com/zh/tutorials/file/dndfiles/
- 载请来源( 前端博客)
- 文章标题: 《JS学习32:html5拖拽图片批量ajax无刷新进度上传》
html5拖拽图片批量ajax无刷新进度上传相关推荐
- JQUERY AJAX无刷新异步上传文件
AJAX无刷新上传文件并显示 http://blog.csdn.net/gao3705512/article/details/9330637?utm_source=tuicool jQuery For ...
- [转]仿163网盘无刷新文件上传系统
原文链接:http://www.cnblogs.com/cloudgamer/archive/2008/10/20/1314766.html 这个仿163网盘无刷新文件上传系统,并没有用使用.net的 ...
- 实用ExtJS教程100例-009:ExtJS Form无刷新文件上传
文件上传在Web程序开发中必不可少,ExtJS Form中有一个filefield字段,用来选择文件并上传.今天我们来演示一下如何通过filefield实现ExtJS Form无刷新的文件上传. 首先 ...
- Asp.Net实现无刷新文件上传并显示进度条(非服务器控件实现)
相信通过Asp.Net的服务器控件上传文件在简单不过了,通过AjaxToolkit控件实现上传进度也不是什么难事,为什么还要自己辛辛苦苦来实现呢?我并不否认"拿来主义",只是我个人 ...
- 在本地测试无组件上传类上传大文件可以,在服务器上就不行,仿163网盘无刷新文件上传系统...
回复 引用 查看 2008-10-20 11:03 | fkeuem 真的很不错.谢谢. 回复 引用 查看 2008-10-20 11:20 | PuserChen 下载了,学 ...
- qstandarditemmodel 重写data函数后无法实现拖拽_实现类似百度网盘上传的功能
今天写到了上传文件部分,记录一下. 前端使用了dropzone.js(用于实现文件上传的JS库),并对此文件进行了细微修改. 说一下思路吧: 首先界面长这样: 点击上传文件,需要弹出一个对话框,对话框 ...
- Javascrpt无刷新文件上传
最近工作中遇到上传文件问题,主要需求是一步点击上传,兼容ie8+,当时用的dojox/form/uploader控件,这两天扒了一下源码,明白了原理拿出来分享一下. 总体思路如下: 1.对于支持XML ...
- 简便无刷新文件上传系统
兼容:ie6/7/8, firefox 3.5.5, opera 10.01, safari 4.0.3, chrome 3.0 效果预览 文件上传 选择文件 重命名 操作 状态 重置 选择文件 ...
- HTML5输入框里加图片代码,做了一个input上传加号框,图片上传后显示在框中,怎么让加号消失?...
CSS代码: .div_imgall {border:1px solid blue;width:100px;height:100px;position:relative;} .input_flie { ...
最新文章
- c语言递归求五阶行列式源代码,久游堂怎么样 -官网
- 一起来看看Fastjson的三种漏洞利用链
- 安装带有调试信息的C库
- 为什么 Kafka 如此之快?
- 【计算机网络复习 数据链路层】3.6.1 局域网
- 京东抢购机器人_戴森、科沃斯、SKG...超多大牌低价秒杀!京东电器等你来
- 百度C2C对决淘宝的两把利器
- error 1044 (42000):access denied for user ''@'localhost' to database 'mysql'
- mysql基础之忘掉密码解决办法及恢复root最高权限办法
- maya破解版安装python_Maya mayapy.exe 安装 Cython,编译 pyd
- 论文翻译(上):Deep Learning Based Semantic Labelling of 3D Point Cloud in Visual SLAM
- 计算机数字公式表白,love密码数字表白 数字表白公式
- 大学计算机基础实训13,东南大学《大学计算机基础》实验13EXCEL图表制作.pdf
- 手机软件测试普通话准确吗,语音输入法到底谁最准?我花了3天时间做了12项对比,评测结果你一定想不到!...
- 使用 Amazon WAF 进行 Captcha 人机验证
- 在word中插入ppt
- java安装有错误码咋办_java编程出现的错误对应的解决方法
- 如何写一手好文章:练习、技巧,以及艺术
- android输入法开发软件,开发安卓系统Android和码输入法软件的全过程(二)
- OpenCV 各版本百度云下载
热门文章
- 信息学奥赛C++语言: 直角三角形
- 阿里云自带的mysql_阿里云Centos使用自带mysql
- qtwebengineprocess已停止工作_windows资源管理器总是停止工作
- JAVA岗位比嵌入式岗位_java嵌入式职业选择?
- C++/OpenCV:同颜色检测提取
- patchGAN再次理解【相比于原始D全图输出true/false,patchGAN可以关注更多的区域】
- 计算机教师自媒体方向,教师和自媒体,我该选择哪个深耕?
- gdt描述_GDT(Global Descriptor Table)全局描述符表
- vue绑定自定义属性(属性值:false),DOM不显示问题
- 阿里云centos云服务器 - 网站搭建教程