原理:
利用Ajax在客户端一直查询服务器端的上传进度,取得进度的状态文本信息(xml,json格式的文本等),然后利用JS解析,显示在前台。
在Struts2. 0中,框架事先已经定义一种监听器:ProgressListener(进度监听器),里面有一个update(long readedBytes, long totalBytes, int currentItem)方法,其中,readedBytes是已经上传到服务器的位数,而totalBytes是上传文件总位数.当文件已二进制的方式上传时,每上传一部分数据,就会调用这个方法一次。故要实现监听进度,必须实现这个接口,并实现update方法,在update方法中保存这个进度到session。当客服端需要进度的信息时,只需要访问某个action,在这个action中读取session中保存的进度状态就可以了.
上传文件可大致分为两个阶段:1. 上传到服务器上,在临时目录中 2.从临时目录中把文件移到指定目录(由自己写的action处理),而struts2.的监听器只监听
第一阶段实现:
第一步:
实现ProgressListener接口,实现update( )方法,详情见action包中的FileUploadListener.java 文件,里面有一个自定义的类:State ,它描述的是进度的状态,详情请看State注释。Update方法要做的就是不断地更新session中的state对象
第二步:
将监听器注入到struts2.0的MultiPartRequest封装类中,客户端发送request到服务器,struts2.0会将request封装成MultiPartRequest。因此必须将监听器注入到MultiPartRequest中。只需要在MultiPartRequest中加入以下两句:
FileUploadListener progressListener = new FileUploadListener(servletRequest);
upload.setProgressListener(progressListener);//添加自己的监听器
所以重新写一个新类MyMultiPartRequest代替MultiPartRequest ,代码与org.apache.struts2.dispatcher.multipart.MultiPartRequest 一样,在方法
private List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) 中加入监听器.
在struts.xml中重新指定MultiPartRequest:
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="requestParser"
class="action.MyMultiPartRequest" scope="default" optional="true" />
<constant name="struts.multipart.handler" value="requestParser" />
到这里,基本完成大部分工作.
附录:
State.java
[java] view plain copy print ?
- public class State {
- private long readedBytes = 0L;
- private long totalBytes = 0L;
- private int currentItem = 0;
- private int rate=0;
- public long getReadedBytes() {
- return readedBytes;
- }
-
- public void setReadedBytes(long readedBytes) {
- this.readedBytes = readedBytes;
- }
-
- public long getTotalBytes() {
- return totalBytes;
- }
-
- public void setTotalBytes(long totalBytes) {
- this.totalBytes = totalBytes;
- }
- public int getCurrentItem() {
- return currentItem;
- }
-
- public void setCurrentItem(int currentItem) {
- this.currentItem = currentItem;
- }
-
- public int getRate() {
- return rate;
- }
- public void setRate(int rate) {
- this.rate = rate;
- }
- }
FileUploadListener.java
[java] view plain copy print ?
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpSession;
- import org.apache.commons.fileupload.ProgressListener;
- import cn.com.order.model.State;
-
- public class FileUploadListener implements ProgressListener {
- private HttpSession session;
- public FileUploadListener(HttpServletRequest request) {
- session = request.getSession();
- State state = new State();
- session.setAttribute("state", state);
- }
- @Override
- public void update(long readedBytes, long totalBytes, int currentItem) {
-
- System.out.println("update:" + readedBytes + ";" + totalBytes + ";" + (int)(((float)readedBytes/(float) ? totalBytes)*100) + ";" + currentItem);
- State state = (State) session.getAttribute("state");
- state.setReadedBytes(readedBytes);
- state.setTotalBytes(totalBytes);
- state.setCurrentItem(currentItem);
- }
- }
MyMultipartRequest.java
[java] view plain copy print ?
- import com.opensymphony.xwork2.inject.Inject;
- import com.opensymphony.xwork2.util.logging.Logger;
- import com.opensymphony.xwork2.util.logging.LoggerFactory;
- import org.apache.commons.fileupload.FileItem;
- import org.apache.commons.fileupload.FileUploadException;
- import org.apache.commons.fileupload.RequestContext;
- import org.apache.commons.fileupload.disk.DiskFileItem;
- import org.apache.commons.fileupload.disk.DiskFileItemFactory;
- import org.apache.commons.fileupload.servlet.ServletFileUpload;
- import org.apache.struts2.StrutsConstants;
- import javax.servlet.http.HttpServletRequest;
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.UnsupportedEncodingException;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.Enumeration;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
-
- public class MyMultiPartRequest implements MultiPartRequest{
- static final Logger LOG = LoggerFactory.getLogger(MultiPartRequest.class);
-
- // maps parameter name -> List of FileItem objects
- protected Map<String,List<FileItem>> files = new HashMap<String,List<FileItem>>();
-
- // maps parameter name -> List of param values
- protected Map<String,List<String>> params = new HashMap<String,List<String>>();
-
- // any errors while processing this request
- protected List<String> errors = new ArrayList<String>();
-
- protected long maxSize;
-
- @Inject(StrutsConstants.STRUTS_MULTIPART_MAXSIZE)
- public void setMaxSize(String maxSize) {
- this.maxSize = Long.parseLong(maxSize);
- }
-
- /**
- * Creates a new request wrapper to handle multi-part data using methods adapted from Jason Pell's
- * multipart classes (see class description).
- *
- * @param saveDir the directory to save off the file
- * @param request the request containing the multipart
- * @throws java.io.IOException is thrown if encoding fails.
- */
- public void parse(HttpServletRequest request, String saveDir) throws IOException {
- try {
- processUpload(request, saveDir);
- } catch (FileUploadException e) {
- LOG.warn("Unable to parse request", e);
- errors.add(e.getMessage());
- }
- }
-
- private void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException {
- for (FileItem item : parseRequest(request, saveDir)) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Found item " + item.getFieldName());
- }
- if (item.isFormField()) {
- processNormalFormField(item, request.getCharacterEncoding());
- } else {
- processFileField(item);
- }
- }
- }
-
- private void processFileField(FileItem item) {
- LOG.debug("Item is a file upload");
-
- // Skip file uploads that don't have a file name - meaning that no file was selected.
- if (item.getName() == null || item.getName().trim().length() < 1) {
- LOG.debug("No file has been uploaded for the field: " + item.getFieldName());
- return;
- }
-
- List<FileItem> values;
- if (files.get(item.getFieldName()) != null) {
- values = files.get(item.getFieldName());
- } else {
- values = new ArrayList<FileItem>();
- }
-
- values.add(item);
- files.put(item.getFieldName(), values);
- }
-
- private void processNormalFormField(FileItem item, String charset) throws UnsupportedEncodingException {
- LOG.debug("Item is a normal form field");
- List<String> values;
- if (params.get(item.getFieldName()) != null) {
- values = params.get(item.getFieldName());
- } else {
- values = new ArrayList<String>();
- }
-
- // note: see http://jira.opensymphony.com/browse/WW-633
- // basically, in some cases the charset may be null, so
- // we're just going to try to "other" method (no idea if this
- // will work)
- if (charset != null) {
- values.add(item.getString(charset));
- } else {
- values.add(item.getString());
- }
- params.put(item.getFieldName(), values);
- }
-
- private List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException {
- DiskFileItemFactory fac = createDiskFileItemFactory(saveDir);
- ServletFileUpload upload = new ServletFileUpload(fac);
- upload.setSizeMax(maxSize);
- /*自己新建监听器*/
- FileUploadListener progressListener = new FileUploadListener(servletRequest);
- upload.setProgressListener(progressListener);//添加自己的监听器
-
- return upload.parseRequest(createRequestContext(servletRequest));
- }
-
- private DiskFileItemFactory createDiskFileItemFactory(String saveDir) {
- DiskFileItemFactory fac = new DiskFileItemFactory();
- // Make sure that the data is written to file
- fac.setSizeThreshold(0);
- if (saveDir != null) {
- fac.setRepository(new File(saveDir));
- }
- return fac;
- }
-
- /* (non-Javadoc)
- * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileParameterNames()
- */
- public Enumeration<String> getFileParameterNames() {
- return Collections.enumeration(files.keySet());
- }
-
- /* (non-Javadoc)
- * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getContentType(java.lang.String)
- */
- public String[] getContentType(String fieldName) {
- List<FileItem> items = files.get(fieldName);
-
- if (items == null) {
- return null;
- }
-
- List<String> contentTypes = new ArrayList<String>(items.size());
- for (FileItem fileItem : items) {
- contentTypes.add(fileItem.getContentType());
- }
-
- return contentTypes.toArray(new String[contentTypes.size()]);
- }
-
- /* (non-Javadoc)
- * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFile(java.lang.String)
- */
- public File[] getFile(String fieldName) {
- List<FileItem> items = files.get(fieldName);
-
- if (items == null) {
- return null;
- }
-
- List<File> fileList = new ArrayList<File>(items.size());
- for (FileItem fileItem : items) {
- File storeLocation = ((DiskFileItem) fileItem).getStoreLocation();
- if(fileItem.isInMemory() && storeLocation!=null && !storeLocation.exists()) {
- try {
- storeLocation.createNewFile();
- } catch (IOException e) {
- if(LOG.isErrorEnabled()){
- LOG.error("Cannot write uploaded empty file to disk: " + storeLocation.getAbsolutePath(),e);
- }
- }
- }
- fileList.add(storeLocation);
- }
-
- return fileList.toArray(new File[fileList.size()]);
- }
-
- /* (non-Javadoc)
- * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileNames(java.lang.String)
- */
- public String[] getFileNames(String fieldName) {
- List<FileItem> items = files.get(fieldName);
-
- if (items == null) {
- return null;
- }
-
- List<String> fileNames = new ArrayList<String>(items.size());
- for (FileItem fileItem : items) {
- fileNames.add(getCanonicalName(fileItem.getName()));
- }
-
- return fileNames.toArray(new String[fileNames.size()]);
- }
-
- /* (non-Javadoc)
- * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFilesystemName(java.lang.String)
- */
- public String[] getFilesystemName(String fieldName) {
- List<FileItem> items = files.get(fieldName);
-
- if (items == null) {
- return null;
- }
-
- List<String> fileNames = new ArrayList<String>(items.size());
- for (FileItem fileItem : items) {
- fileNames.add(((DiskFileItem) fileItem).getStoreLocation().getName());
- }
-
- return fileNames.toArray(new String[fileNames.size()]);
- }
-
- /* (non-Javadoc)
- * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameter(java.lang.String)
- */
- public String getParameter(String name) {
- List<String> v = params.get(name);
- if (v != null && v.size() > 0) {
- return v.get(0);
- }
-
- return null;
- }
-
- /* (non-Javadoc)
- * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterNames()
- */
- public Enumeration<String> getParameterNames() {
- return Collections.enumeration(params.keySet());
- }
-
- /* (non-Javadoc)
- * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterValues(java.lang.String)
- */
- public String[] getParameterValues(String name) {
- List<String> v = params.get(name);
- if (v != null && v.size() > 0) {
- return v.toArray(new String[v.size()]);
- }
-
- return null;
- }
-
- /* (non-Javadoc)
- * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getErrors()
- */
- public List getErrors() {
- return errors;
- }
-
- /**
- * Returns the canonical name of the given file.
- *
- * @param filename the given file
- * @return the canonical name of the given file
- */
- private String getCanonicalName(String filename) {
- int forwardSlash = filename.lastIndexOf("/");
- int backwardSlash = filename.lastIndexOf("\\");
- if (forwardSlash != -1 && forwardSlash > backwardSlash) {
- filename = filename.substring(forwardSlash + 1, filename.length());
- } else if (backwardSlash != -1 && backwardSlash >= forwardSlash) {
- filename = filename.substring(backwardSlash + 1, filename.length());
- }
-
- return filename;
- }
-
- /**
- * Creates a RequestContext needed by Jakarta Commons Upload.
- *
- * @param req the request.
- * @return a new request context.
- */
- private RequestContext createRequestContext(final HttpServletRequest req) {
- return new RequestContext() {
- public String getCharacterEncoding() {
- return req.getCharacterEncoding();
- }
-
- public String getContentType() {
- return req.getContentType();
- }
-
- public int getContentLength() {
- return req.getContentLength();
- }
-
- public InputStream getInputStream() throws IOException {
- InputStream in = req.getInputStream();
- if (in == null) {
- throw new IOException("Missing content in the request");
- }
- return req.getInputStream();
- }
- };
- }
- }
UploadAction.java
[java] view plain copy print ?
- import java.io.*;
-
- import javax.servlet.http.HttpSession;
-
- import org.apache.struts2.ServletActionContext;
-
- import com.opensymphony.xwork2.ActionSupport;
-
- public class FileProgressUploadAction extends ActionSupport{
- private File file;
- private String fileFileName;
- private String fileContentType;
- public File getFile() {
- return file;
- }
-
- public void setFile(File file) {
- this.file = file;
- }
-
-
- public String getFileFileName() {
- return fileFileName;
- }
-
- public void setFileFileName(String fileFileName) {
- this.fileFileName = fileFileName;
- }
-
- public String getFileContentType() {
- return fileContentType;
- }
-
- public void setFileContentType(String fileContentType) {
- this.fileContentType = fileContentType;
- }
-
- @Override
- public String execute(){
-
- try {
- System.out.println("file:"+file);
- InputStream is=new FileInputStream(file);
- String root=ServletActionContext.getRequest().getRealPath("/upload");
- System.out.println("root:"+root);
-
- System.out.println("name:"+this.fileFileName);
- System.out.println("type:"+this.fileContentType);
- File destFile=new File(root,this.fileFileName);
-
- OutputStream os=new FileOutputStream(destFile);
-
- byte [] b=new byte[1024*1024*10];
- int length=0;
- while(true){
- length=is.read(b);
- if(length<0)
- break;
- os.write(b,0,length);
- }
-
- is.close();
- os.close();
- }catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return "success";
- }
- }
ProgressAction.java(更新进度条进度的Action)注:jsp使用setInterval用Ajax技术定时访问该Action,就可以定时获取到上传进度
[java] view plain copy print ?
- import org.apache.struts2.ServletActionContext;
- import javax.servlet.http.HttpSession;
-
- import com.opensymphony.xwork2.ActionSupport;
-
- public class FileProgressAction extends ActionSupport{
- private State state;
-
- public State getState() {
- return state;
- }
-
- public void setState(State state) {
- this.state = state;
- }
-
-
- @Override
- public String execute() throws Exception {
- // TODO Auto-generated method stub
- HttpSession session=ServletActionContext.getRequest().getSession();
- this.state=(State)session.getAttribute("state");
- if(state==null){
- System.out.println("action is null");
- state=new State();
- state.setCurrentItem(0);
- }else{
-
- Double a=Double.parseDouble(state.getReadedBytes()+"");
- Double b=Double.parseDouble(state.getTotalBytes()+"");
- double result=a/b*100;
- state.setRate((int)result);
- System.out.println("action:"+state.getRate());
- }
- return "success";
- }
- }
Upload.jsp(Ajax文件上传)需要下载ajaxfileupload.js,jquery-ui.js(显示进度条)插件
[java] view plain copy print ?
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <!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>文件上传</title>
- <link rel="stylesheet" href="js/jquery-ui-1.9.2.css" />
- <script src="js/jquery-1.8.3.js"></script>
- <script src="js/jquery-ui-1.9.2.js"></script>
- <script type="text/javascript" src="js/ajaxfileupload.js"></script>
- <script type="text/javascript">
- var time = 0;
- function ajaxFileUpload()
- {
- $( "#progressbar" ).progressbar({
- value: 0
- });
- time = window.setInterval(progress,1000);
- $.ajaxFileUpload
- (
- {
- url:'hello.action',
- secureuri:false,
- fileElementId:'fileToUpload',//fileToUpload是input file 标签的id值
- dataType: 'multipart/form-data',
- success: function (data, status)
- {
- if(typeof(data.error) != 'undefined')
- {
- if(data.error != '')
- {
- alert(data.error);
- }else
- {
- alert(data.msg);
- }
- }
- },
- error: function (data, status, e)
- {
- alert(e);
- }
- }
- )
- return false;
- }
-
- function progress(){
- $.ajax({
- url:"progress.action",
- dataType: 'json',
- success:function(data){
- $("#loading").html(data.state.rate);
- $( "#progressbar" ).progressbar({
- value: data.state.rate
- });
- if(data.state.rate == 100){
- clearInterval(time);
- }
- },
- error:function(){
- alert("error");
- }
- });
- }
- </script>
- </head>
- <body>
- <form name="form" action="" method="POST" enctype="multipart/form-data">
-
- <input type="file" id="fileToUpload" name="fileToUpload"/>
- <input type="text" name="username"/>
- <input type="button" value="上传" onclick="ajaxFileUpload()"/>
- </form>
- <div id="progressbar"></div>
- </body>
- </html>
-