WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。本文主要介绍如何使用该组件实现对图片文件的上传下载和图片回显。本文基于webuploader-0.1.5版本介绍该组件js实现部分。
一、官网Demo说明:
解压官网下载下来的压缩包,打开webuploader-0.1.5/examples/image-upload/index.html即可看到前端界面效果,选择图片,开始上传,没错,上传失败。因为没有设置有效的请求路径,打开统计目录下的upload.js文件,该文件也是实现和扩展webuploader的核心文件,找到组件初始化的地方,如下:
// 实例化
uploader = WebUploader.create({
pick: {
id: '#filePicker',
label: '点击选择图片'
},
formData: {
uid: 123
},
dnd: '#dndArea',
paste: '#uploader',
swf: '../../dist/Uploader.swf',
chunked: false,
chunkSize: 512 * 1024,
server: '../../server/fileupload.php',
// runtimeOrder: 'flash',
// accept: {
// title: 'Images',
// extensions: 'gif,jpg,jpeg,bmp,png',
// mimeTypes: 'image/*'
// },
// 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候,把图片打开。
disableGlobalDnd: true,
fileNumLimit: 300,
fileSizeLimit: 200 * 1024 * 1024, // 200 M
fileSingleSizeLimit: 50 * 1024 * 1024 // 50 M
});
此时,前端界面无需修改,只需要修改初始化部分的server为有效的图片上传请求路径,当然图片上传的后台需要提前实现,可以采用阿里云OSS实现或其他方式。图片传是最基本的功能,该组件也实现的很好,有图片类型、图片大小、图片数量的限制等,这里不细说。
二、图片回显
一般情况下,引入该插件实现图片上传到服务器,不会有什么问题。但是我们在使用原生插件的过程中会发现一个问题,每次刷新页面再次进入图片上传界面时,之前上传的页面是不是没有了,正在苦恼如何将之前上传的图片显示出来时,我们发现其核心文件upload.js中提供了一个函数addFile,事实上当我们上传图片前将图片添加到容器中时,也是调用的该方法实现图片的预览,那么我们就会想,只要能拿到之前上传的图片文件,然后循环调用该方法,不就能实现图片回显了。问题又来了,如何拿到之前上传的图片文件呢?
针对上述问题,其实只要我们能够获取到保存图片的url,一切问题也都迎刃而解了,以本人实际项目的实现情况为例,图片保存再阿里云OSS上,通过在upload.js中扩展一个查询图片url的接口就能实现图片回显。具体实现:
1)往项目后台发请求,查询url,
2)通过url获取图片文件,
3)调用addFile方法添加文件到容器中。
//图片回显
var getFileBlob = function (filename, imageUrl, cb) {
var xhr = new XMLHttpRequest();
xhr.open("GET", imageUrl);
xhr.responseType = "blob";
xhr.addEventListener('load', function() {
cb(xhr.response);
});
xhr.send();
};
var blobToFile = function (blob, name) {
blob.lastModifiedDate = new Date();
blob.name = name;
return blob;
};
var getFileObject = function(filename, imageUrl, cb) {
getFileBlob(filename, imageUrl, function (blob) {
cb(blobToFile(blob, filename));
});
};
$.ajax({
type:"POST",
url:getattachmenturl,
data:{
outId:outId
},
async:false,
dataType: "json",
success:function(data){
console.log(data);
$.each(data,function(n,value) {
var filename = value.name;
$.ajax({
type:"POST",
url:getimageurl,
data:{localUrl:value.url},
async:false,
success:function(imageUrl){
getFileObject(filename, imageUrl, function (fileObject) {
var wuFile = new WebUploader.Lib.File(WebUploader.guid('rt_'),fileObject);
var file = new WebUploader.File(wuFile);
file.state = 'finish';
uploader.addFiles(file);
$('#' + file.id).find('.progress').fadeOut();
var selector = 'li#'+file.id;
var $div = $(selector).find('div.file-panel');
$div.after('');
})
}
})
});
}
});
代码说明:
a、设置图片文件的状态为"finish",file.state = 'finish';
b、往后台请求图片的url地址以及参数需要自定义,这里说明一下阿里云OSS保存图片的url格式一般是endpoint+"/"+bucketName+"/"+localUrl,endpoint和bucketName直接查看阿里云应用即可拿到,localUrl为本地保存的图片url,当前也可以直接将完整的url保存在本地。
c、之所以调用addFiles方法而不是addFile方法,是因为addFiles实际上最后还是调用的addFile方法,只是在调用之前做了一些预处理,比如图片类型检测,大小判断等。
d、$('#' + file.id).find('.progress').fadeOut(),这里一起说一下Demo中的一个问题,使用Demo实现图片上传之后,会发现图片下方的进度条变成白色,但是不会消失,这里就是将进度条灰化,隐藏进度条,同样,只需要对Demo扩展一个状态处理接口即可实现完成上传之后隐藏进度条,uploadComplete为图片上传完成之后的一个状态,通过给改状态绑定一个处理接口即可实现。(突然想到如果addFiles之前直接设置图片的状态为"uploadComplete"是不是也能实现隐藏进度条呢?有待验证。)
uploader.on('uploadComplete', function (file) {
$('#' + file.id).find('.progress').fadeOut();
});
e、进度条灰化后面的三行代码主要是为了实现给回显图片加上已上传成功的标志,设置对应"class"为"success"即可。
说明:笔者使用的阿里云OSS保存图片,在图片回显时通过XMLHttpRequest发送GET请求到阿里云OSS请求图片时,会报403错误"No 'Access-Control-Allow-Origin' header is present on the requested resource",查阅资料发现需要在阿里云应用中设定一下跨域规则,一般是将跨域规则中的来源开放即可,笔者直接设定为"*",并允许GET和POST请求,当然考虑到安全问题直接设定为"*"并不一定是最好的办法,这里也不过多讨论。
三、图片下载
由于图片下载不是该组件实现的常规功能,所以要实现对图片的下载,也只需要在upload.js中扩展一个图片下载的接口,发送下载图片的请求到后台即可。阿里云OSS图片下载实现参考:
HttpServletResponse response = getHttpServletResponse(); //自定义实现
OSSObject ossObject = AliYunOSSUtil.downLoad("图片下载url,前文有描述");
BufferedReader reader =null;
InputStream inputStream = null;
BufferedOutputStream outputStream = null;
try {
reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
inputStream = ossObject.getObjectContent();
//缓冲文件输出流
outputStream=new BufferedOutputStream(response.getOutputStream());
// 为防止 文件名出现乱码
response.setContentType("application/doc");
response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(attachment.getName(),"UTF-8"));
byte[] car = new byte[1024];
int L;
while((L = inputStream.read(car)) != -1){
if (car.length!=0){
outputStream.write(car, 0,L);
}
}
if(outputStream!=null){
outputStream.flush();
}
}finally {
if (outputStream!=null){
outputStream.close();
}
if (inputStream!=null){
inputStream.close();
}
if (reader!=null){
reader.close();
}
if (ossObject!=null){
ossObject.close();
}
}
四、错误码说明
同样还是以Demo实现图片上传为例,当我们添加重复图片时,组件会弹出"F_DUPLICATE"这么一个错误,不太友好。别急,该组件同样提供了错误处理函数,只需要绑定一个onError函数,将组件原生的错误码做个适配即可实现自定义错误码。
//错误提示
uploader.onError = function( code ) {
if(code == "F_DUPLICATE"){
alert("系统提示:请不要选择重复文件!");
}else if(code == "Q_EXCEED_NUM_LIMIT"){
alert("系统提示:最多上传5张图片!");
}else if(code == "Q_EXCEED_SIZE_LIMIT"){
alert("系统提示:所选附件总大小不可超过" + allMaxSize + "M哦!
换个小点的文件吧!");
}else{
alert( 'Eroor: ' + code );
}
};
五、图片回显时重复添加图片问题
最后一点,是笔者在应用过程中发现的,不知道其他人在使用的过程中有没有遇到过。还是以Demo实现为例来说,当我们添加图片然后上传,添加的过程中组件能区分重复图片,没问题。第一次上传完了第二次再添加图片时,组件也能区分重复图片,也没问题。但是在我们刷新页面,根据第二点实现图片回显之后会发现,如果此时我们再添加图片,重复的图片也能添加进来,这里提供一种笔者的解决思路和办法。
一开始想着在addFile图片到容器中时,遍历一遍容器中已有的图片,如果发现有名称相同的图片存在,则跳出addFile并提示图片重复,但是在addFile函数中添加循环判断处理之后发现,即使有重复图片跳出addFile,重复的图片还是会加入到容器中。通过调试发现webuploader.js中有一个_addFile函数,添加图片时,会先进入该函数,然后触发"fileQueued"状态绑定的addFile方法来添加图片到容器中,所以就算addFile失败,图片还是会在之前就加入到队列中了,最终会加入到容器中。
看到_addFile函数我们会发现有一个触发"beforeFileQueued"状态的绑定函数,至此,也就有了解决办法,在图片进入队列之前,绑定一个函数对图片是否重复进行判断,如果失败就return,这样就不会进入队列从而加入容器中了。
uploader.on('beforeFileQueued', function (file) {
var repeat = 0;
$('.filelist').find('li').each(function() {
if(file.name == $(this).find('.title').html())
{
repeat = 1;
return;
}
})
if(repeat == 1){
alert("系统提示:请不要选择重复文件!");
return false;
}
});
其实,对于图片回显之后还能添加重复图片的问题,笔者还是觉得在回显图片的时候可能还有什么地方需要进行设置来保证后续再添加时自动排除重复图片问题。由于是第一次使用该组件,加上时间关系,暂时采用该办法来保证回显之后不能再添加重复图片。
总的来说,第一次使用来组件来实现图片上传下载以及回显,并且该组件也支持复制和拖拽,效果还是很不错的,中间也是遇到一些问题,也都一一解决。后续还会继续深入学习了解该组件的其他功能。如文中有什么错误或者问题,还望各位能指出,谢谢。