图片上传是前端开发过程最常碰见的功能之一。现在有很多成熟的图片上传插件uploadify、file upload、 webuploader 等,除了这些HMTL5技术上也支持了这一需求,今天我们就来聊一聊这些是怎么实现的。以及根据他的原理自己写一个上传的功能。
一、uploadify插件
uploadify是一款免费的jquery的上传插件,有flash版本和html5版本,html5版本支持鼠标拖拽上传,因为一些HTML5版的一些兼容性,以flash版本使用居多,所以下面介绍flash版本的实现
1)html代码
2)JS代码
$(function(){
$("#file_upload").uploadify({
'auto':true, //是否为自动上传
'swf':'/uploadify/uploadify.swf', //上传的flash插件
'uploader':'/uploadify/uploadify.php', //后台处理上传文件的脚本
'buttonImage' :'/uploadify/browser.btn.png', //上传按钮的外观为自定义图片
'buttonText':'上传图片', //上传按钮的文字信息
'debug':true, //开启uploadify的调试模式
'fileObjName':'myFile', //服务器端接收的对象,如$_POST['myFile']
'fileSizeLimit':'5MB', //上传文件的最大值
'fileTypeDesc':'支持的格式:', //可选择文件类型说明
'fileTypeExts':'*.jpg;*.png;*.bmp;*.gif', //允许的文件扩展名
'formData':{'somekey':'somevalue','someotherkey':1}, //上传文件时上传需要的表单值
'height':'50px', //按钮的高度
'width':'100px', //按钮的宽度
'method':'post', //上传的方式,默认为post
'multi':true, //允许批量上传,默认为true
'preventCaching':true, //是否阻止缓存
'progressData':'percentage', //上传队列显示为速度还是百分比
'onDialogOpen':function(){ alert('ok');} //打开上传按钮浏览框时触发事件
'onUploadStart':function(){ } //文件开始上传时触发事件
'onUploadSuccess':function(file,data,response){ //文件上传成功时触发事件
//成功以后这里继续操作
},
'onFallback':function(){ //检测FLASH失败调用
alert("请安装FLASH控件后再试。");
},
//正在上传。。
'onSelect': function(file){
$("#alertDiv").text("正在上传...");
$("#alertDiv").show();
},
'onSelectError':function(file, errorCode, errorMsg){
switch(errorCode) {
case -100:
alert("上传的文件数量已经超出系统限制的"+$('#file_upload').uploadify('settings','queueSizeLimit')+"个文件!");
break;
case -110:
alert("文件 ["+file.name+"] 大小超出系统限制的"+$('#file_upload').uploadify('settings','fileSizeLimit')+"大小!");
break;
case -120:
alert("文件 ["+file.name+"] 大小异常!");
break;
case -130:
alert("文件 ["+file.name+"] 类型不正确!");
break;
}
},
//所有文件处理完成时
'onQueueComplete': function(queueData){
alert(queueData.uploadsSuccessful);
}
});
});
3)PHP后代的代码
public function uploadify(){
if(!empty($_FILES){
import('ORG.Net.UploadFile');
$name = time().rand();
$upload = new UploadFile();
$upload->maxSize = 3145728;
$upload->allowExts = array('jpg','gif','png','jpeg');
$upload->savePath = './Public/upload/';
$upload->saveRule = $name;
if(!$upload->upload()){
echo $upload->getErrorMsg();
}else{
$info = $upload->getUploadFileInfo();
echo $info[0]['svaename'];
}
}
}
uploadify插件分HTML5版本和FLAS版本,在chorme等为了安全性考试的浏览器中访问时直接flash被屏弊掉,而在firfox等浏览器如果不默认安装flash,则该功能不能实现,所以我体验不太好。如果研用HTML5版的一些低版本不支持
二、jquery file upload插件
1)基本使用
jQuery File Upload主要使用了XHR作为上传方式,并且利用了相当多的现代浏览器功能,所以可以实现诸如批量上传、超大文件上传、图片预览、拖拽上传、上传进度显示、跨域上传等功能。
Query File Upload包含了一堆文件,最核心的部分必须包括以下文件:
1.jQuery核心库 建议使用jQuery 1.8以上版本
2.jquery.ui.widget.js : jQuery UI Widget
3.jquery.iframe-transport.js : 扩展iframe数据传输
4.jquery.fileupload.js : jQuery File Upload核心类
5. jquery.xdr-transport.js 在IE下应载入此文件解决跨域问题
引入文件以后html如下:
js代码如下:
$('#fileupload').fileupload();
2)一些扩展
这就完成了一个简单的上传设置。只不过这只是一个最简单的使用方式,它完全实现一些扩展如下:
上传完成以后的操作:
$('#fileupload').fileupload({
done: function (e, data) {
//do something
}
});
进度条的设置
$('#fileupload').fileupload('option', {
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
console.log(progress + '%');
}
});
更多扩展可以查看官方网站API
3)一些注意的坑
json数据传送响应
由于采用XHR在传递数据,服务器端返回的通常是JSON格式的响应,但是IE会将这些JSON响应误认为是文件传输,然后直接弹出下载框询问是否需要下载。
解决方法:
是必须将相应的Http Head从 Content-Type: application/json更改为 Content-Type: text/plain
定制自己的UI困难
因为官方特意给了一个UI上的设置,这里的东西不可局部更改,更改时导致UI混乱。因此得他细找出来功能点进行指定的UI覆盖才可以,具体细节这里不在详细讲述了
三、webuploader插件
webUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件,使用WebUploader还可以批量上传文件、支持缩略图等等众多参数选项可设置,以及多个事件方法可调用,你可以随心所欲的定制你要的上传组件。
1)html代码
2)js代码
3)php代码
public function webuploader() {
$upload = new \Think\Upload(); // 实例化上传类
$upload->maxSize = 3145728 ; // 设置附件上传大小
$upload->exts = array('jpg', 'gif', 'png', 'jpeg'); // 设置附件上传类型
$upload->rootPath = './Public/Uploads/'; // 设置附件上传根目录
$upload->savePath = ''; // 设置附件上传(子)目录
$upload->autoSub = false; // 关闭子目录
// 上传文件
$info = $upload->upload();
if(!$info) { // 上传错误提示错误信息
$this->error($upload->getError());
}else{ // 上传成功 获取上传文件信息
$pathArr = array();
foreach($info as $file){
array_push($pathArr, "Public/Uploads/".$file['savepath'].$file['savename']);
}
echo json_encode($pathArr);
}
}
webUploader是html5和flash两者做了兼容,对高低版本都做了一些良好的支持,而且官网API写的详细,实例很多,容易实用。在这么多插件中主动推荐使用这个插件
四、HMTL5怎么实现
这里提到了html5所以你懂的,这个不要考虑PC端了。一般都是手机端实现图片上传才会用HMTL5实现,具体实现方法如下:
实现HMTL5图片上传肯定要用到FileReader来读取本地文件,FileReade的属性和方法如下:
属性,所有属性都是只读的:
FileReader.error,读取文件时,出现的DOMError。
FileReader.readyState,读取状态;0,没有数据加载;1,数据正在加载;2,读取已经完成。
FileReader.result,文件内容;该属性只在读取操作完成后才有效,并且格式取决于读取时使用的方法。
事件:
FileReader.onabort,读取操作中止。
FileReader.onerror,读取出现错误。
FileReader.onload,读取成功完成后。
FileReader.onloadstart,读取开始。
FileReader.onloadend,读取完成,无论是否读取成功。
FileReader.onprogress,当读取Blob内容时。
方法:
FileReader.abort() 中止读取。然后readyState变为2。
FileReader.readAsArrayBuffer() 将文件读取成ArrayBuffer。
FileReader.readAsBinaryString() 读取成二进制字符串。
FileReader.readAsDataURL() 读取成DataURL。
FileReader.readAsText() 读取成文本。
预览功能实现:
预览功能实现:
function upload() {
var xhr = new XMLHttpRequest();
var progress = document.getElementById("progress")
progress.style.display = "block";
xhr.upload.addEventListener("progress", function(e) {
if (e.lengthComputable) {
var percentage = Math.round((e.loaded * 100) / e.total);
progress.value = percentage;
}
}, false);
xhr.upload.addEventListener("load", function(e){
console.log("上传结束...")
}, false);
xhr.open("POST", "upload");
xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText); // handle response.
progress.style.display = "none";
progress.value = 0;
}
};
var file = document.getElementById("imgFile");
var fd = new FormData();
fd.append(file.files[0].name, file.files[0]);
xhr.send(fd);
}
五、分析下原理自已动手写一个
上面我们基本总结了图片上传的经常用的插件及HTML5实现的方式。每个都有优点和缺点,一般为了兼容低版本都是走的flash,一旦走的是flash就会出现安装插件的请求,这个体验不好,在一上次做需求的时候需要用到多图片上传,索性自己写了个。
实现原理:
通过Iframe+form的结合。设置input file enctype="multipart/form-data" target="ajaxUpload" 属性允许选择多张图片上传,模拟input file上传功能安钮,当input file change的时候 通过form action把当前的图片上传给后台,后台设置,当前端iframe加载完成时获取server端返回的生成服务器上的img URL。解析数据生成远程的img.程现到页面上出来。然后做删除滚动一系列的后续工作等。
Html代码:
JS代码:
var ShensuFn=function(remote){
this.attr= {
'_shenText' : $('._shenText'),
'_shenSelect' : $('._shenSelect'),
'_shenfile' : $('#fileName'),
'_shenMsg' : $('#returnMsg'),
'_sheniframe' : $('#iframebox'),
'_shenBox' : $('.scrollBox'),
'_shenUrl' : $('.scrollBox ul'),
'_shenDaText' : $('#data_text')
};
/*上传图片相关逻辑*/
imgUpload:function(){
//当选择当前文件时提交
var lName;
var that=this;
this.attr._shenfile.on('change',function(){
//没有选择图片时
var filVal = $(this).val();
if(filVal==''){
return;
}
// 提示信息
that.attr._shenMsg.show();
$("#fileForm").submit();
that.attr._shenfile.val('');
})
//提交加载完成Iframe时设置图片
this.attr._sheniframe.on('load',function(){
//取出iframe中的数据
var body = $(window.frames['ajaxUpload'].document.body);
var aLiLeng = that.attr._shenUrl.children().not("#shen-from").length;
that.attr._shenDaText.text(body.context.textContent);
var _data=JSON.parse(that.attr._shenDaText.text());
var res=_data.data;
//如果数据为空或者不存在
if((!res && _data.error!='-1') || (res.length<1 && _data.error!='-1')){
return;
}
//有错误信息时提示
if(_data.error=='-1'){
alert(_data.errormsg);
}
//图片总数量超过9张时
if((aLiLeng+res.length)>9){
alert('你上传的图片张数过多,已为您添加前9张图片')
//删除多余图片数
res.splice(9-aLiLeng);
$("#fileForm").hide();
//图片总数量等于9张时
}else if((aLiLeng+res.length)==9){
$("#fileForm").hide();
}
//隐藏数据
that.attr._shenDaText.hide();
//生成img
for(var i=0;i
$("
}
var aLiLeng = that.attr._shenUrl.children().not("#shen-from").length;
//UL的宽度
that.attr._shenUrl.css('width',((aLiLeng+1)*(118+15))+'px');
//UL的scrollLeft
aLiLeng>2?(that.attr._shenBox.scrollLeft((aLiLeng-2)*(118+15))):(that.attr._shenBox.scrollLeft(0));
//loading隐藏
that.attr._shenMsg.hide();
//关闭当前的图片并删除
that.attr._shenUrl.find('.close-btn').click(function(){
$(this).parent().remove();
var aLiLeng = that.attr._shenUrl.children().not("#shen-from").length;
//删除后计算UL的宽度
that.attr._shenUrl.css('width',((aLiLeng+1)*(118+15))+'px');
//删除后计算UL的left
aLiLeng>2?that.attr._shenBox.scrollLeft((aLiLeng-2)*(118+15)):that.attr._shenBox.scrollLeft(0);
//删除时检测当前的图片个数控制是否显示上传安钮
if(aLiLeng<9){
$("#fileForm").show();
}
});
})
}
new ShensuFn();
总结:如上所述,总结所有目前图片上传的方法。及自己手动写的方式等,其中插件推荐使用webUploader,做了HMTL5 和FLASH的兼容处理。下面手动写的iframe的方式也是一种很好的实现方法,用它实现了局部刷新的功能。因为iframe加载整个页面不会刷新,而且避免再次出现了flash这个东西让用户安装的不好的体验方式,唯一要求的就是后台的一些配合,不过做开发需要后台的配合这个也合乎常理。