Code Bye

Struts2文件上传进度条

我按照网上做法,一步步实现的,可是为什么总是不能成功呢。那么要是有兴趣也试试看啊。
下面我贴出原文:
   原理:
     利用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.的监听器只监听第一阶段。
   
实现:
(源代码下载: http://download.csdn.net/source/3568014)
第一步:
    实现ProgressListener接口,实现update( )方法,详情见action包中的FileUploadListener.java 文件,里面有一个自定义的:State ,它描述的是进度的状态,详情请看State注释。Update方法要做的就是不断地更新session中的state对象 代码如
 

  8.	public class FileUploadListener implements ProgressListener{  
9.	    private HttpSession session;  
10.	      
11.	    public FileUploadListener(HttpServletRequest request) {  
12.	           session = request.getSession();  
13.	           State state = new State();  
14.	           session.setAttribute("state", state);  
15.	    }  
16.	    @Override  
17.	    public void update(long readedBytes, long totalBytes, int currentItem) {  
18.	        // TODO Auto-generated method stub  
19.	           System.out.println("update:"+readedBytes+";"+totalBytes+";"+currentItem);  
20.	             
21.	           State state = (State) session.getAttribute("state");  
22.	             
23.	           state.setReadedBytes(readedBytes);  
24.	           state.setTotalBytes(totalBytes);  
25.	           state.setCurrentItem(currentItem);  
26.	    }  
27.	}  
 

State:

  3.	public class State {  
4.	    private long readedBytes = 0L;/*已经上传的位数*/  
5.	    private long totalBytes = 0L;/*文件所占位数*/  
6.	    private int currentItem = 0;  
7.	    private int rate=0; /*上传百分比*/  
8.	    public long getReadedBytes() {  
9.	        return readedBytes;  
10.	    }  
11.	      
12.	    public void setReadedBytes(long readedBytes) {  
13.	        this.readedBytes = readedBytes;  
14.	    }  
15.	      
16.	    public long getTotalBytes() {  
17.	        return totalBytes;  
18.	    }  
19.	      
20.	    public void setTotalBytes(long totalBytes) {  
21.	        this.totalBytes = totalBytes;  
22.	    }  
23.	    public int getCurrentItem() {  
24.	        return currentItem;  
25.	    }  
26.	      
27.	    public void setCurrentItem(int currentItem) {  
28.	        this.currentItem = currentItem;  
29.	    }  
30.	  
31.	    public int getRate() {  
32.	        return rate;  
33.	    }  
34.	    public void setRate(int rate) {  
35.	        this.rate = rate;  
36.	    }     
37.	}  

第二步:
   将监听器注入到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)  中加入监听器.

 29.	public class MyMultiPartRequest implements MultiPartRequest{  
30.	static final Logger LOG = LoggerFactory.getLogger(MultiPartRequest.class);  
31.	      
32.	    // maps parameter name -> List of FileItem objects  
33.	    protected Map<String,List<FileItem>> files = new HashMap<String,List<FileItem>>();  
34.	  
35.	    // maps parameter name -> List of param values  
36.	    protected Map<String,List<String>> params = new HashMap<String,List<String>>();  
37.	  
38.	    // any errors while processing this request  
39.	    protected List<String> errors = new ArrayList<String>();  
40.	      
41.	    protected long maxSize;  
42.	  
43.	    @Inject(StrutsConstants.STRUTS_MULTIPART_MAXSIZE)  
44.	    public void setMaxSize(String maxSize) {  
45.	        this.maxSize = Long.parseLong(maxSize);  
46.	    }  
47.	  
48.	    /**  
49.	     * Creates a new request wrapper to handle multi-part data using methods adapted from Jason Pell""s  
50.	     * multipart classes (see class description).  
51.	     *  
52.	     * @param saveDir        the directory to save off the file  
53.	     * @param request the request containing the multipart  
54.	     * @throws java.io.IOException  is thrown if encoding fails.  
55.	     */  
56.	    public void parse(HttpServletRequest request, String saveDir) throws IOException {  
57.	        try {  
58.	            processUpload(request, saveDir);  
59.	        } catch (FileUploadException e) {  
60.	            LOG.warn("Unable to parse request", e);  
61.	            errors.add(e.getMessage());  
62.	        }  
63.	    }  
64.	  
65.	    private void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException {  
66.	        for (FileItem item : parseRequest(request, saveDir)) {  
67.	            if (LOG.isDebugEnabled()) {  
68.	                LOG.debug("Found item " + item.getFieldName());  
69.	            }  
70.	            if (item.isFormField()) {  
71.	                processNormalFormField(item, request.getCharacterEncoding());  
72.	            } else {  
73.	                processFileField(item);  
74.	            }  
75.	        }  
76.	    }  
77.	  
78.	    private void processFileField(FileItem item) {  
79.	        LOG.debug("Item is a file upload");  
80.	  
81.	        // Skip file uploads that don""t have a file name - meaning that no file was selected.  
82.	        if (item.getName() == null || item.getName().trim().length() < 1) {  
83.	            LOG.debug("No file has been uploaded for the field: " + item.getFieldName());  
84.	            return;  
85.	        }  
86.	  
87.	        List<FileItem> values;  
88.	        if (files.get(item.getFieldName()) != null) {  
89.	            values = files.get(item.getFieldName());  
90.	        } else {  
91.	            values = new ArrayList<FileItem>();  
92.	        }  
93.	  
94.	        values.add(item);  
95.	        files.put(item.getFieldName(), values);  
96.	    }  
97.	  
98.	    private void processNormalFormField(FileItem item, String charset) throws UnsupportedEncodingException {  
99.	        LOG.debug("Item is a normal form field");  
100.	        List<String> values;  
101.	        if (params.get(item.getFieldName()) != null) {  
102.	            values = params.get(item.getFieldName());  
103.	        } else {  
104.	            values = new ArrayList<String>();  
105.	        }  
106.	  
107.	        // note: see http://jira.opensymphony.com/browse/WW-633  
108.	        // basically, in some cases the charset may be null, so  
109.	        // we""re just going to try to "other" method (no idea if this  
110.	        // will work)  
111.	        if (charset != null) {  
112.	            values.add(item.getString(charset));  
113.	        } else {  
114.	            values.add(item.getString());  
115.	        }  
116.	        params.put(item.getFieldName(), values);  
117.	    }  
118.	  
119.	    private List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException {  
120.	        DiskFileItemFactory fac = createDiskFileItemFactory(saveDir);  
121.	        ServletFileUpload upload = new ServletFileUpload(fac);  
122.	        upload.setSizeMax(maxSize);  
123.	        /*自己新建监听器*/  
124.	        FileUploadListener progressListener = new FileUploadListener(servletRequest);  
125.	        upload.setProgressListener(progressListener);//添加自己的监听器  
126.	          
127.	        return upload.parseRequest(createRequestContext(servletRequest));  
128.	    }  
129.	  
130.	    private DiskFileItemFactory createDiskFileItemFactory(String saveDir) {  
131.	        DiskFileItemFactory fac = new DiskFileItemFactory();  
132.	        // Make sure that the data is written to file  
133.	        fac.setSizeThreshold(0);  
134.	        if (saveDir != null) {  
135.	            fac.setRepository(new File(saveDir));  
136.	        }  
137.	        return fac;  
138.	    }  
139.	  
140.	    /* (non-Javadoc)  
141.	     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileParameterNames()  
142.	     */  
143.	    public Enumeration<String> getFileParameterNames() {  
144.	        return Collections.enumeration(files.keySet());  
145.	    }  
146.	  
147.	    /* (non-Javadoc)  
148.	     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getContentType(java.lang.String)  
149.	     */  
150.	    public String[] getContentType(String fieldName) {  
151.	        List<FileItem> items = files.get(fieldName);  
152.	  
153.	        if (items == null) {  
154.	            return null;  
155.	        }  
156.	  
157.	        List<String> contentTypes = new ArrayList<String>(items.size());  
158.	        for (FileItem fileItem : items) {  
159.	            contentTypes.add(fileItem.getContentType());  
160.	        }  
161.	  
162.	        return contentTypes.toArray(new String[contentTypes.size()]);  
163.	    }  
164.	  
165.	    /* (non-Javadoc)  
166.	     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFile(java.lang.String)  
167.	     */  
168.	    public File[] getFile(String fieldName) {  
169.	        List<FileItem> items = files.get(fieldName);  
170.	  
171.	        if (items == null) {  
172.	            return null;  
173.	        }  
174.	  
175.	        List<File> fileList = new ArrayList<File>(items.size());  
176.	        for (FileItem fileItem : items) {  
177.	            File storeLocation = ((DiskFileItem) fileItem).getStoreLocation();  
178.	            if(fileItem.isInMemory() && storeLocation!=null && !storeLocation.exists()) {  
179.	                try {  
180.	                    storeLocation.createNewFile();  
181.	                } catch (IOException e) {  
182.	                    if(LOG.isErrorEnabled()){  
183.	                        LOG.error("Cannot write uploaded empty file to disk: " + storeLocation.getAbsolutePath(),e);  
184.	                    }  
185.	                }  
186.	            }  
187.	            fileList.add(storeLocation);  
188.	        }  
189.	  
190.	        return fileList.toArray(new File[fileList.size()]);  
191.	    }  
192.	  
193.	    /* (non-Javadoc)  
194.	     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileNames(java.lang.String)  
195.	     */  
196.	    public String[] getFileNames(String fieldName) {  
197.	        List<FileItem> items = files.get(fieldName);  
198.	  
199.	        if (items == null) {  
200.	            return null;  
201.	        }  
202.	  
203.	        List<String> fileNames = new ArrayList<String>(items.size());  
204.	        for (FileItem fileItem : items) {  
205.	            fileNames.add(getCanonicalName(fileItem.getName()));  
206.	        }  
207.	  
208.	        return fileNames.toArray(new String[fileNames.size()]);  
209.	    }  
210.	  
211.	    /* (non-Javadoc)  
212.	     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFilesystemName(java.lang.String)  
213.	     */  
214.	    public String[] getFilesystemName(String fieldName) {  
215.	        List<FileItem> items = files.get(fieldName);  
216.	  
217.	        if (items == null) {  
218.	            return null;  
219.	        }  
220.	  
221.	        List<String> fileNames = new ArrayList<String>(items.size());  
222.	        for (FileItem fileItem : items) {  
223.	            fileNames.add(((DiskFileItem) fileItem).getStoreLocation().getName());  
224.	        }  
225.	  
226.	        return fileNames.toArray(new String[fileNames.size()]);  
227.	    }  
228.	  
229.	    /* (non-Javadoc)  
230.	     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameter(java.lang.String)  
231.	     */  
232.	    public String getParameter(String name) {  
233.	        List<String> v = params.get(name);  
234.	        if (v != null && v.size() > 0) {  
235.	            return v.get(0);  
236.	        }  
237.	  
238.	        return null;  
239.	    }  
240.	  
241.	    /* (non-Javadoc)  
242.	     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterNames()  
243.	     */  
244.	    public Enumeration<String> getParameterNames() {  
245.	        return Collections.enumeration(params.keySet());  
246.	    }  
247.	  
248.	    /* (non-Javadoc)  
249.	     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterValues(java.lang.String)  
250.	     */  
251.	    public String[] getParameterValues(String name) {  
252.	        List<String> v = params.get(name);  
253.	        if (v != null && v.size() > 0) {  
254.	            return v.toArray(new String[v.size()]);  
255.	        }  
256.	  
257.	        return null;  
258.	    }  
259.	  
260.	    /* (non-Javadoc)  
261.	     * @see 283.	    }  	}  

在上面代码中看上去很多 其实就这里是关键:
 /*自己新建监听器*/             FileUploadListener progressListener = new FileUploadListener(servletRequest);             upload.setProgressListener(progressListener);//添加自己的监听器    

第三步:  在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" />

  
到这里,基本完成大部分工作.
 
接下来是业务,写两个Action  ,一个是查询进度的FileProgressAction ,一个是处理上传文件FileProgressUploadAction . FileProgressAction从Session中读入state对象,返回客户端。
在客户端方面,点击”提交”的时候,需要完成两步工作,1.不断到服务器查询进度2.上传文件,因为本人JS不是很好,就潦草写了一些,主要用的是Jquery框架。然后查询进度时,state对象以json数据方式传递到客户端的。

 http://download.csdn.net/source/3568014  
这是该作者的源码
           

我按照这个文章的做法一步一步做下去。但是为什么就说不能成功呢。不知道哪里错了还是怎么的?求大神们给点好方法
网上一搜 “struts2带进度条的文件上传”很多文章,我看了基本都是这个方法来做的!为什么我的不能成功??

75分
xml指定自己的文件上传解析有错误
<constant name=”struts.multipart.parser” value=”requestParser” /> 
引用 4 楼 suciver 的回复:

xml指定自己的文件上传解析有错误
<constant name=”struts.multipart.parser” value=”requestParser” /> 

哇真的是大神啊!多谢多谢 困扰很多天了,以前根本不进去我的监听类里面去。现在这样一改就对了
但是我想问下:网上很多很多文章都是
<constant name=”struts.multipart.handler” value=”requestParser” /> 
可是这样就是不对。我想问问是那些网上的文章都是错误的还是struts2版本变了 引起的不同。
还有阁下为何如此厉害 怎么学的  嘻嘻!


5分
请教,如果多文件上传,如何给每个上传的文件一个进度条,又如果总进度给个进度条呢?
引用 4 楼 suciver 的回复:

xml指定自己的文件上传解析类有错误
<constant name=”struts.multipart.parser” value=”requestParser” /> 

这样修改过后是可以执行了。但是为什么我action里面拿到的进度总是100% 我输出了一下,发现其先执行监听类,等监听类里面文件进度为100%的时候,才会去调用我的获取进度条的action 方法,请问这个该如何解决??

引用 7 楼 anybyb 的回复:
Quote: 引用 4 楼 suciver 的回复:

xml指定自己的文件上传解析类有错误
<constant name=”struts.multipart.parser” value=”requestParser” /> 

这样修改过后是可以执行了。但是为什么我action里面拿到的进度总是100% 我输出了一下,发现其先执行监听类,等监听类里面文件进度为100%的时候,才会去调用我的获取进度条的action 方法,请问这个该如何解决??

你取进度的action应该是另一个action。因为struts2文件上传的时候你到文件action的时候其实文件已经传到服务器了。不知道你前台是怎么传的建议使用jquery.form.js的ajaxSubmit来传输文件。这样可以异步传输。此时你ajax请求取进度的action应该在文件上传之前就要用js的setInteval每多少时间就去取。如果你等文件上传的请求到达action的时候文件其实已经传完了所以你取到的进度一直都是100%。当然这里小文件的话会传输太快建议用大点的文件才能看出效果

引用 8 楼 suciver 的回复:
Quote: 引用 7 楼 anybyb 的回复:
Quote: 引用 4 楼 suciver 的回复:

xml指定自己的文件上传解析类有错误
<constant name=”struts.multipart.parser” value=”requestParser” /> 

这样修改过后是可以执行了。但是为什么我action里面拿到的进度总是100% 我输出了一下,发现其先执行监听类,等监听类里面文件进度为100%的时候,才会去调用我的获取进度条的action 方法,请问这个该如何解决??

你取进度的action应该是另一个action。因为struts2文件上传的时候你到文件action的时候其实文件已经传到服务器了。不知道你前台是怎么传的建议使用jquery.form.js的ajaxSubmit来传输文件。这样可以异步传输。此时你ajax请求取进度的action应该在文件上传之前就要用js的setInteval每多少时间就去取。如果你等文件上传的请求到达action的时候文件其实已经传完了所以你取到的进度一直都是100%。当然这里小文件的话会传输太快建议用大点的文件才能看出效果

补充下如果用小文件也可以看出效果就是在你监听器中每次都让线程sleep下也就是增加Thread.sleep(1000)每次解析都睡眠一秒这样10MB左右的文件也能看出进度条的效果

引用 9 楼 suciver 的回复:
Quote: 引用 8 楼 suciver 的回复:
Quote: 引用 7 楼 anybyb 的回复:
Quote: 引用 4 楼 suciver 的回复:

xml指定自己的文件上传解析类有错误
<constant name=”struts.multipart.parser” value=”requestParser” /> 

这样修改过后是可以执行了。但是为什么我action里面拿到的进度总是100% 我输出了一下,发现其先执行监听类,等监听类里面文件进度为100%的时候,才会去调用我的获取进度条的action 方法,请问这个该如何解决??

你取进度的action应该是另一个action。因为struts2文件上传的时候你到文件action的时候其实文件已经传到服务器了。不知道你前台是怎么传的建议使用jquery.form.js的ajaxSubmit来传输文件。这样可以异步传输。此时你ajax请求取进度的action应该在文件上传之前就要用js的setInteval每多少时间就去取。如果你等文件上传的请求到达action的时候文件其实已经传完了所以你取到的进度一直都是100%。当然这里小文件的话会传输太快建议用大点的文件才能看出效果

补充下如果用小文件也可以看出效果就是在你监听器中每次都让线程sleep下也就是增加Thread.sleep(1000)每次解析都睡眠一秒这样10MB左右的文件也能看出进度条的效果

恩 谢谢 解决了!谢谢啦 呵呵!

兄台怎么解决的?

CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明Struts2文件上传进度条