这两天在找一个可以实现上传时显示进度的ajax库,看了很多10 best ajax uploader之类的文章,发现里面很多链接都点不进去,或者网站文档和demo做得很烂,我这人选开源软件用,一定会看他的文档做得怎么样,做得不全就懒得研究了,swfupload文档做得也不好,例子太少,没有讲解,不过本着矬子里面拔高个的原则只能将就着用它了。外国有个人实现了一个swfuload的jquery插件,主要是完善swfupload的事件机制的,可以点这里去看看,结合这个插件,做多文件上传加进度条是件很easy的事情。官网的例子偏偏没有后端是java的例子,我又google了一番,试用了一下commons-fileupload,感觉很方便,于是就整出了一个例子来,前端是参照这个做了些修改。btw,iteye有好几篇是讲swfupload+java实现上传的,后端是个jsp自己实现的保存上传文件的,我下载了一下代码,发现一模一样,也不知是谁抄谁的,都宣称原创,好笑。
1.前面选择上传文件的页面upload.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> <link href="css/default.css" rel="stylesheet" type="text/css"/> <script type="text/javascript" src="js/swfupload.js"></script> <script type="text/javascript" src="js/swfupload.queue.js"></script> <script type="text/javascript" src="js/fileprogress.js"></script> <script type="text/javascript" src="js/handlers.js"></script> <script type="text/javascript" src="js/jquery-1.4.3.js"></script> <script type="text/javascript" src="js/jquery.swfupload.js"></script> <style type="text/css"> #swfupload-control p { margin: 10px 5px; font-size: 0.9em; } #log { margin: 0; padding: 0; width: 500px; } #log li { list-style-position: inside; margin: 2px; border: 1px solid #ccc; padding: 10px; font-size: 15px; font-family: Arial, Helvetica, sans-serif; color: #333; background: #fff; position: relative; } #log li .progressbar { border: 1px solid #333; height: 18px; background: #fff; } #log li .progress { background: rgba(104, 133, 127, 0.52); width: 0%; height: 18px; } #log li p { margin: 0; line-height: 18px; } #log li.success { border: 1px solid #339933; background: #ccf9b9; } #log li span.stopUpload { position: absolute; top: 5px; right: 5px; width: 20px; height: 20px; background: url('js/swfupload/cancel.png') no-repeat; cursor: pointer; } #img span.cancel { background: url('js/swfupload/cancel.png') no-repeat; } </style> <script type="text/javascript"> $(function () { $('#swfupload-control').swfupload({ upload_url:"handler/doUpload", file_post_name:'uploadfile', file_size_limit:"20000 MB", file_types:"*.*", file_types_description:"All files", flash_url:"js/swfupload.swf", button_image_url:'js/swfupload/wdp_buttons_upload_114x29.png', button_width:114, button_height:29, button_placeholder:$('#button')[0], }) .bind('fileQueued', function (event, file) { var listitem = '<li id="' + file.id + '" >' + 'File: <em>' + file.name + '</em> (' + Math.round(file.size / 1024) + ' KB) <span class="progressvalue" ></span>' + '<div class="progressbar" ><div class="progress" ></div></div>' + '<p class="status" >Pending</p>' + '<span class="stopUpload" > </span>' + '</li>'; $('#log').append(listitem); $('li#' + file.id + ' .stopUpload').bind('click', function () { //Remove from queue on cancel click var swfu = $.swfupload.getInstance('#swfupload-control'); swfu.cancelUpload(file.id); $('#log li#' + file.id).find('p.status').text('Canceled'); $('#log li#' + file.id).find('span.stopUpload').css('background-image','none') $('#log li#' + file.id).find('span.stopUpload').css('cursor','defult') // $('li#' + file.id).slideUp('fast'); }); // start the upload since it's queued $(this).swfupload('startUpload'); }) .bind('fileQueueError', function (event, file, errorCode, message) { //alert('Size of the file '+file.name+' is greater than limit'); alert(errorCode+":"+message) }) .bind('fileDialogComplete', function (event, numFilesSelected, numFilesQueued) { $('#queuestatus').text('Files Selected: ' + numFilesSelected + ' / Queued Files: ' + numFilesQueued); }) .bind('uploadStart', function (event, file) { $('#log li#' + file.id).find('p.status').text('Uploading...'); $('#log li#' + file.id).find('span.progressvalue').text('0%'); $('#log li#' + file.id).find('span.cancel').hide(); }) .bind('uploadProgress', function (event, file, bytesLoaded) { //Show Progress var percentage = Math.round((bytesLoaded / file.size) * 100); $('#log li#' + file.id).find('div.progress').css('width', percentage + '%'); $('#log li#' + file.id).find('span.progressvalue').text(percentage + '%'); }) .bind('uploadSuccess', function (event, file, serverData) { var item = $('#log li#' + file.id); item.find('div.progress').css('width', '100%'); item.find('span.progressvalue').text('100%'); item.addClass('success').find('p.status').html('Done!!! '); $('#log li#' + file.id).find('span.stopUpload').css('background-image','none') ; $('#log li#' + file.id).find('span.stopUpload').css('cursor','default') ; }) .bind('uploadComplete', function (event, file) { // upload has completed, try the next one in the queue $(this).swfupload('startUpload'); }) }); </script> </head> <body> <div id="swfupload-control"> <p>Select files of any type, muliple files are allowed to select every time.</p> <input type="button" id="button"/> <p id="queuestatus"></p> <ol id="log"></ol> </div> </body> </html>
取消按钮的那个class如果叫cancel(原始例子是叫这个名字的),是不好用的,html里总是有一些诡异的保留字在等着我们,我改成stopUpload就好用了。
2.后端保存文件的servlet
package com.offspring.servlet; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.List; public class Upload extends javax.servlet.http.HttpServlet { public static final int MB = 1024 * 1024; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, IOException { DiskFileItemFactory fileItemFactory = new DiskFileItemFactory(10 * MB, new File("d:/temp4")); ServletFileUpload uploader = new ServletFileUpload(fileItemFactory); uploader.setSizeMax(1200 * MB); List fields = null; try { fields = uploader.parseRequest(request); } catch (FileUploadException e) { e.printStackTrace(); return; } Iterator iter = fields.iterator(); while (iter.hasNext()) { FileItem item = (FileItem) iter.next(); if (!item.isFormField()) { try { processUploadedFile(item); } catch (Exception e) { e.printStackTrace(); return; } } } response.getOutputStream().println("200 OK"); } private void processUploadedFile(FileItem item) throws Exception { String basePath = "d:\\temp\\upload\\"; item.write(new File(basePath + item.getName())); System.out.println("write file to '"+basePath + item.getName()+"'"); } }
运行时截图:
你同时选多个文件也是可以的,不过仍然一次上传一个,一个传完了,下一个会自动开启上传。
3.实现进度条的原理
swfupload提供了很多事件,结合jquery可以绑定相应的回调函数, 比如uploadProgress会在上传过程中定期被触发,你在这个回调里写个用css更新进度条宽度就OK了。
.bind('uploadProgress', function (event, file, bytesLoaded) { //Show Progress var percentage = Math.round((bytesLoaded / file.size) * 100); $('#log li#' + file.id).find('div.progress').css('width', percentage + '%'); $('#log li#' + file.id).find('span.progressvalue').text(percentage + '%'); })
不过记得在linux上 uploadProgress 只会在上传结束时才触发,这是linux上flash的一个bug。本来写得很多,在firefox上上传附件不好用,然后文字还变成了乱码,这是怎么搞的?
代码可以在附件中找到 。