8

基于HTML5的可预览多图片Ajax上传

 3 years ago
source link: https://www.zhangxinxu.com/wordpress/2011/09/%e5%9f%ba%e4%ba%8ehtml5%e7%9a%84%e5%8f%af%e9%a2%84%e8%a7%88%e5%a4%9a%e5%9b%be%e7%89%87ajax%e4%b8%8a%e4%bc%a0/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

by zhangxinxu from http://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=1923

一、关于图片上传什么什么的

在XHTML的时代,我们使用HTML file控件上传图片一次只能上传一张。要一次上传多图,做法是借助于flash。例如swfupload.js。可惜,使用复杂的点,比如flash文件需与页面同父文件夹,JavaScript文件大小也很可观。

我之前曾翻译编辑过一篇“Ajax Upload多文件上传插件”的文章,此插件的亮点是使用隐藏的iframe框架页面模拟ajax上传,但是,实际上,还是一次只能上传1张图片,可以多次上传而已。

HTML5是个好东东,其中之一就是支持多图片上传,而且支持ajax上传,而且支持上传之前图片的预览,而且支持图片拖拽上传,纯粹利用file控件实现,JS代码寥寥,想不让人称赞都难啊!

二、demo页面

如果您手头上的浏览器是最新的FireFox或是Chrome浏览器,您可以狠狠地点击这里:基于HTML5的多图Ajax上传demo

在demo页面中,您可以点击file控件上传多图,如下(FireFox 6截图示意,下同):

选择多图 张鑫旭-鑫空间-鑫生活

如果有非图片文件或是图片尺寸过大,会弹出提示:

不符合要求文件提示 张鑫旭-鑫空间-鑫生活

或者您可以直接将桌面上的图片拖到接受拖拽的区域处:

图片拖拽示意 张鑫旭-鑫空间-鑫生活

释放后图片就可以直接预览了(此时还未上传到服务器上):

图片上传前预览 张鑫旭-鑫空间-鑫生活

此时图片可提前删除,也可以直接上传,例如,我们点击上传按钮,很快的,图片上传成功啦:)!

图片上传中进度显示示意 张鑫旭-鑫空间-鑫生活

上传后的页面地址就返回了,如下:

图片上传成功后返回的地址 张鑫旭-鑫空间-鑫生活

此时,对应的upload文件夹下面这张图片就有了:

文件夹下面上传的对应图片张鑫旭-鑫空间-鑫生活

注意:鄙人博客空间大小有限,我会定时清理该图片文件夹,so, 诸位不要把这里当做免费的图片托管场所哦~~

三、核心骨架脚本简单剖析

首先是文件上传的一个core文件,是前两个晚上慢慢吞吞整出来的。文件名是: zxxFile.js (可右键……下载)

此文件就几K,百来行代码,主要负责文件上传相关的逻辑(选择、删除之类),原生JS,因此,兼容jQuery,YUI, MooYools等。zxxFile.js其实是个小巧的骨架文件,肉体等则需要另外添加。

zxxFile.js其实就是个小小对象而已:

var ZXXFILE = {
    //骨架们...
}

下表显示为ZXXFILE对象的属性(骨架)及其对应的内容含义等。

属性或方法 默认方法或值 释义 fileInput null DOM元素。表file控件元素。 dragDrop null DOM元素。表拖放感应区域元素。 upButton null DOM元素。提交的按钮元素。 url "" 字符串。表示文件ajax上传的地址 fileFilter [] 过滤后的文件对象数组,一般不参与初始化。可用来判断当前列表的数目。 filter function(files) {
  return files;
} 函数。用来过滤选择的文件列表。例如只能是选择图片,或是大小尺寸限制等。支持一个参数(files),为文件对象数组,需返回数组。 onSelect function() {} 函数。当本地文件被选择之后执行的回调。支持一个参数(files),为文件对象数组。 onDelete function() {} 函数。当某一个上传文件上传成功之后(自动)或被删除(手动)的时候执行的方法。接受一个参数(file),表当前删除文件。 onDragOver function() {} 函数。当本地文件被拖到拖拽敏感元素上面时执行的方法。方法中的this指该敏感元素,也就是上面的dragDrop元素。 onDragLeave function() {} 函数。当本地文件离开拖拽敏感元素时执行的方法。方法中的this指该敏感元素,也就是上面的dragDrop元素。 onProgress function() {} 函数。文件上传过程中执行的回调方法。接受三个参数(file, loaded, total),分别表示当前上传文件对象,已上传字节数,文件总字节数。 onSuccess function() {} 函数。当前文件上传成功执行的回调方法。接受两个参数(file, response),表示当前上传成功的文件对象和后台返回的字符内容。 onFailure function() {} 函数。当前文件上传失败执行的回调方法。接受两个参数(file, response),表示当前上传失败的文件对象和后台返回的字符内容。 onComplete function() {} 函数。当前文件对象列表全部上传完毕执行的回调方法。无可用参数。 funDragHover 略 方法。文件拖拽相关。非可用API。 funGetFiles 略 方法。获取选择或拖拽文件。非可用API。 funDealFiles 略 方法。对选择文件进行处理。非可用API。 funDeleteFile 略 方法。删除列表中的某个文件。外部可用API,在手动删除某文件时需调用此方法。 funUploadFile 略 方法。文件上传相关。非可用API。 init 略 方法。初始化,主要是一个元素的事件绑定。非可用API。

补充说明:上面多次提到的file参数指的是file object对象,该对象的属性值有name, size, type等,然后,在zxxFile.js中,其还多了个方便元素定位的index索引属性。

显然,只有骨架基本上做不了什么事件。demo页面之所以有效果,就是其按照上面的骨架,根据实际的需求增加了血肉。您可以直接“右键-查看页面源代码”一览所有相关JavaScript。或者看我下面一点一点婆妈的讲述。

我们按照上面表格中的骨架进行示意。demo页面借用了比较流行的jQuery库,骨架+血肉 = 插件,当然,demo页面并不是奔着插件去的(虽然只需稍加修改),因为页面的UI显然不够插件的份。也就是说,利用zxxFile.js骨架,配合点你自己属性的JavaScript库就可以书写属于你自己的基于HTML5的多文件Ajax上传插件啦!

四、demo页面的些代码

demo页面代码整体逻辑如下:

var params = {
    //血肉们
};
ZXXFILE = $.extend(ZXXFILE, params);
ZXXFILE.init();

fileInput
首先是file控件元素,如下:

fileInput: $("#fileImage").get(0)

因为是DOM元素,所以应用了jQuery的get方法。下面两个参数同。

demo页面中的file控件元素支持多文件选择,其隐藏的玄机就是下面代码中大红高亮的部分:

<input id="fileImage" type="file" size="30" name="fileselect[]" multiple />

dragDrop和upButton
拖拽区域和上传按钮(默认隐藏):

dragDrop: $("#fileDragArea").get(0),
upButton: $("#fileSubmit").get(0)

url
Ajax上传地址,没什么好说的,取的是表单的action地址:

url: $("#uploadForm").attr("action")

filter方法
对选择的文件进行过滤。file控件什么文件都能选,而demo页面是图片上传相关的demo;空间大小有限,超大尺寸的图片还是挡着为好。显然,要对上传文件进行过滤。于是,就有了如下的过滤脚本:

filter: function(files) {
    var arrFiles = [];
    for (var i = 0, file; file = files[i]; i++) {
        if (file.type.indexOf("image") == 0) {
            if (file.size >= 512000) {
                alert('您这张"'+ file.name +'"图片大小过大,应小于500k');	
            } else {
                arrFiles.push(file);	
            }			
        } else {
            alert('文件"' + file.name + '"不是图片。');	
        }
    }
    return arrFiles;
}

zxxFile.js会自动对过滤后的文件对象列表进行整合,以准确上传。

onSelect方法
文件(这里就是图片)选择后执行的方法。在本实例页面中,onSelect方法的主要任务就是本地图片在浏览器中的预览。本地图片上传之前在浏览器中预览的核心脚本就是:

var reader = new FileReader(), htmlImage;
reader.onload = function(e) {
    htmlImage = '<img src="'+ e.target.result +'" />';
}
reader.readAsDataURL(file);

在本demo页面中,该部分完成脚本如下,虽好像有些长度,其实内容就是装载一些HTML代码而已:

onSelect: function(files) {
    var html = '', i = 0;
    //等待载入gif动画
    $("#preview").html('<div class="upload_loading"></div>');
    var funAppendImage = function() {
        file = files[i];
        if (file) {
            var reader = new FileReader()
            reader.onload = function(e) {
                html = html + '<div id="uploadList_'+ i +'" class="upload_append_list"><p><strong>' + file.name + '</strong>'+ 
                    '<a href="javascript:" class="upload_delete" title="删除" data-index="'+ i +'">删除</a><br />' +
                    '<img id="uploadImage_' + i + '" src="' + e.target.result + '" class="upload_image" /></p>'+ 
                    '<span id="uploadProgress_' + i + '" class="upload_progress"></span>' +
                '</div>';
                
                i++;
                funAppendImage();
            }
            reader.readAsDataURL(file);
        } else {
            //图片相关HTML片段载入
            $("#preview").html(html);
            if (html) {
                //删除方法
                $(".upload_delete").click(function() {
                    ZXXFILE.funDeleteFile(files[parseInt($(this).attr("data-index"))]);
                    return false;	
                });
                //提交按钮显示
                $("#fileSubmit").show();	
            } else {
                //提交按钮隐藏
                $("#fileSubmit").hide();	
            }
        }
    };
    //执行图片HTML片段的载人
    funAppendImage();		
}

细心的你可能发现到上面的HTML元素中基本上都用到了i这个索引,作用是方便后面删除可以找到相应的元素。
然后,还有一个需要注意的就是删除事件——执行了ZXXFILE.funDeleteFile()方法,这是必须的,真正将图片从文件列表中删除,同时用来触发onDelete方法的回调。

onDelete方法
图片上传完毕或是删除之时执行飞方法。本实例是让其渐隐:

onDelete: function(file) {
    $("#uploadList_" + file.index).fadeOut();
}

onDragOver方法
文件拖到拖拽元素上时执行的方法,本实例就是增加了个类名,如下:

onDragOver: function() {
    $(this).addClass("upload_drag_hover");
}

onDragLeave方法
文件移出元素上时执行的方法,本实例就是去掉了个类名,如下:

onDragLeave: function() {
    $(this).addClass("upload_drag_hover");
}

onProgress方法
上传中触发的方法。本demo效果就是图片左上角有个有着圆角黑色半透明背景元素,里面的百分比值不断增加。代码:

onProgress: function(file, loaded, total) {
    var eleProgress = $("#uploadProgress_" + file.index), percent = (loaded / total * 100).toFixed(2) + '%';
    eleProgress.show().html(percent);
}

onSuccess方法
当前图片上传成功后执行的方法。本demo就是提示返回的图片地址信息:

onSuccess: function(file, response) {
    $("#uploadInf").append(""<p>上传成功,图片地址是:" + response + ""</p>");
}

onFailure方法
图片上传嗝屁时尿出的方法。本demo为提示,然后图片浅透明:

onFailure: function(file) {
    $("#uploadInf").append("<p>图片" + file.name + "上传失败!</p>");	
    $("#uploadImage_" + file.index).css("opacity", 0.2);
}

onComplete方法
当所有图片都上传完毕之后,本实例页面把file控件的value值置空,同时按钮隐藏了:

onComplete: function() {
    //提交按钮隐藏
    $("#fileSubmit").hide();
    //file控件value置空
    $("#fileImage").val("");
    $("#uploadInf").append("<p>当前图片全部上传完毕,可继续添加上传。</p>");
}

PHP页面相关代码

$fn = (isset($_SERVER['HTTP_X_FILENAME']) ? $_SERVER['HTTP_X_FILENAME'] : false);
if ($fn) {
    file_put_contents(
        'uploads/' . $fn,
        file_get_contents('php://input')
    );
    echo "http://www.zhangxinxu.com/study/201109/uploads/$fn";
    exit();
}

以上就是主要的些功能或交互代码。至于CSS样式部分以及HTML代码中的一些细节我就懒得捡芝麻了。您有兴趣可以通过查看源代码观摩观摩。

五、当下HTML5文件Ajax上传应用范围

不仅IE浏览器不支持,最新win下的Safari浏览器,或是Opera都不完全完全支持HTML5的可预览多图片Ajax上传,那我们还有学习这个干嘛呢?至少现在鸟这个是没有的。

确实,我们对外的一些项目,给广大用户使用的web页面使用这项技术为时过早。但是,对于公司的内网项目,应用这个绝对OK的。我发现了个很奇怪的问题,很多时候,内网的网页都是支持低版本的IE较好,对于现代浏览器却不支持。这完全是走在错误的道路上。

最近,我们公司开始内网项目变革,开始基于Chrome等现代浏览器进行内网开发(当然,IE浏览器也是可以使用的),内部工作人员强制使用Chrome浏览器。就我们公司而言,反响很不错,无论是UI效果,交互还是速度方面的体验都反馈不错。

显然,至少在我们公司,以后要给内网的编辑或是小秘书们做个多图上传的功能,就直接可以使用HTML5文件上传了,也就是本文所说的内容。简单,速度,快捷,会让你体会到开发是件快乐而有价值感的事情。

补充说下,本文的demo页面更多的是用来示例,其中若有纰漏还望见谅。zxxFile.js也是刚刚出炉,未经历练。欢迎提出宝贵意见,不甚感谢。

新增于2016-09-20
很多小伙伴跪求php代码,我的回复是自己想办法,原因有两个,一是自己是业余,代码仅仅实现基本功能;二是暴露后端代码存在不安全隐患。

大家不妨问问专门从事后端开发的同学,分分钟可以得到回复的。

(本篇完)1f44d.svg 是不是学到了很多?可以分享到微信
1f44a.svg 有话要说?点击这里


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK