前两天,公司一同事要做一个包含有文件上传的功能模块,问我采取哪种技术比较好。由于项目的技术架构是ssi,于是就建议他直接使用struts提供的FormFile。可是在他动手开发的过程中,却遇到了一些实际的问题,后来又找了我几次。最后我也专门想了想,其实struts提供的上传技术,虽然操作起来确实很简单,但是在某些方面却也存在一定的问题,使开发人员特别是新人很抓狂,甚至觉得无所适从。下面就简单聊一聊这些问题。
struts的上传其实就是对commons fileupload技术做了一个集成。在集成的过程中,像什么交换目录啊、文件大小控制啊之类的工作都帮我们完成了;而我们在使用的时候,这些问题都不需要考虑,甚至可能几行代码就搞定了。下面就是使用struts实现的一个简单的文件上传功能实例:
jsp页面:
1
2
3
4
5<form action="upload_action_form.do" method="post" enctype="multipart/form-data"> 标题:<input type="text" name="title"><br> 文件:<input type="file" name="myfile"><br> <input type="submit" value="提交"> </form>
Form类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import org.apache.struts.action.ActionForm; import org.apache.struts.upload.FormFile; /** * * @description: * @author: bruce.yang * @date:2013-7-27 下午3:30:14 * @version v1.0 * */ public class UploadActionForm extends ActionForm { private String title; //必须用FormFile接收文件 private FormFile myfile; //... ... getter,setter方法 }
Action类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/** * * @description: * @author: bruce.yang * @date:2013-7-27 下午3:32:45 * @version v1.0 * */ public class UploadAction2 extends Action { @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { UploadActionForm uaf = (UploadActionForm) form; String title = uaf.getTitle(); System.out.println(title); FileOutputStream fos = new FileOutputStream("d:\" + uaf.getMyfile().getFileName()); fos.write(uaf.getMyfile().getFileData()); fos.flush(); fos.close(); return mapping.findForward("upload"); } }
结果页面ret_upload.jsp:
1
2
3
4
5
6
7<body> 上传成功!<br> 标题:${uploadForm.title}<br> 文件名:${uploadForm.myfile.fileName} }<br> 文件大小:${uploadForm.myfile.fileSize }<br> 文件类型:${uploadForm.myfile.contentType } </body>
struts-config.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14<struts-config> <form-beans> <form-bean name="uploadForm" type="com.bruceyang.struts.form.UploadActionForm"></form-bean> </form-beans> <action-mappings> <action path="/upload_action_form" type="com.bruceyang.struts.action.UploadAction" name="uploadForm" scope="request" > <forward name="upload" path="/ret_upload.jsp"></forward> </action> </action-mappings> </struts-config>
web.xml想必大家都熟知,就不在这浪费篇幅了。
从上面代码中我们可以看到:struts使用FormFile来声明文件,我们在Action中直接操作FormFile实例就可以取得有关文件的数据、文件名、文件大小、上传类型等。而这只是在假设一切正常的情况下实现的,但是如果在实际运行中出现上传文件太大、服务器磁盘不足、enctype不对等异常情况时,
struts是怎么处理的呢?
从struts源码 org.apache.struts.config.ControllerConfig类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/** * The fully qualified Java class name of the MultipartRequestHandler * class to be used. */ protected String multipartClass = "org.apache.struts.upload.CommonsMultipartRequestHandler"; public String getMultipartClass() { return (this.multipartClass); } public void setMultipartClass(String multipartClass) { if (configured) { throw new IllegalStateException("Configuration is frozen"); } this.multipartClass = multipartClass; }
可以看出,如果没有在配置文件中配置指定的处理类,那么struts处理文件上传的实现类是CommonsMultipartRequestHandler,那么查其源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67/** * The default value for the maximum allowable size, in bytes, of an * uploaded file. The value is equivalent to 250MB. */ public static final long DEFAULT_SIZE_MAX = 250 * 1024 * 1024; /** * The default value for the threshold which determines whether an uploaded * file will be written to disk or cached in memory. The value is equivalent * to 250KB. */ public static final int DEFAULT_SIZE_THRESHOLD = 256 * 1024; //.......省略部分 /** * Parses the input stream and partitions the parsed items into a set of * form fields and a set of file items. In the process, the parsed items * are translated from Commons FileUpload <code>FileItem</code> instances * to Struts <code>FormFile</code> instances. * * @param request The multipart request to be processed. * * @throws ServletException if an unrecoverable error occurs. */ public void handleRequest(HttpServletRequest request) throws ServletException { // Get the app config for the current request. ModuleConfig ac = (ModuleConfig) request.getAttribute( Globals.MODULE_KEY); // Create and configure a DIskFileUpload instance. DiskFileUpload upload = new DiskFileUpload(); // The following line is to support an "EncodingFilter" // see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=23255 upload.setHeaderEncoding(request.getCharacterEncoding()); // Set the maximum size before a FileUploadException will be thrown. upload.setSizeMax(getSizeMax(ac)); // Set the maximum size that will be stored in memory. upload.setSizeThreshold((int) getSizeThreshold(ac)); // Set the the location for saving data on disk. upload.setRepositoryPath(getRepositoryPath(ac)); // Create the hash tables to be populated. elementsText = new Hashtable(); elementsFile = new Hashtable(); elementsAll = new Hashtable(); // Parse the request into file items. List items = null; try { items = upload.parseRequest(request); } catch (DiskFileUpload.SizeLimitExceededException e) { // Special handling for uploads that are too big. request.setAttribute( MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED, Boolean.TRUE); return; } catch (FileUploadException e) { log.error("Failed to parse multipart request", e); throw new ServletException(e); } // Partition the items into form fields and files. Iterator iter = items.iterator(); while (iter.hasNext()) { FileItem item = (FileItem) iter.next(); if (item.isFormField()) { addTextParameter(request, item); } else { addFileParameter(item); } } }
可以看出,struts默认处理上传文件最大是250M,而一旦上传文件过大超过了最大值,struts仅仅是在request的Attribute中添加属性值对MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED-Boolean.TRUE,并未将异常情况带回到Action!struts这种“极其不负责任”的行为直接导致了,开发人员不知道在什么时候告诉用户他传的文件过大了!即使可以在struts的配置文件中指定上传文件的最大值。更过分的是,对于上传过程中出现的其它问题,struts压根就没有去处理它,而是在自己的日志中记录了一下,就直接抛给了上一层了。
而对开发人员而言,有时候是一定要获得这些(甚至是全部)异常,该咋办呢?
对不起,您只有自己定制MultipartRequestHandler了。由于时间的关系,我没有定制该类,在这里仅给大家提供个思路,希望朋友们,能够帮我完成并恳请与我交流。在写之前,您务必明白commons-fileupload上传控件在上传过程中到底可能抛出那些异常?那么在定制MultipartRequestHandler.handleRequest的时候,你使用try/catch尽可能的捕获所有异常,并放到request的attribute里去就哦了。完了之后,在struts-config.xml中使用<controller>标签指定让struts使用你的处理类。这样,我们再用FormFile上传文件,一旦上传过程中出现了异常,就会被写入request的attributs里。而在action类中,只需要获取各种异常信息,即可随时做相应处理并给用户返回相应的提示。
时间关系,我在那个项目中是向 struts做了妥协,并没有定制新的MultipartRequestHandler,而只是获取了MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED来判断文件是否过大,对于其他的异常信息,一律作为上传失败处理,类似:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32/** * * @description: * @author: bruce.yang * @date:2013-7-27 下午3:32:45 * @version v1.0 * */ public class UploadAction extends Action { @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { try { UploadActionForm uaf = (UploadActionForm) form; String title = uaf.getTitle(); System.out.println(title); if (request.getAttribute(MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED) != null) { request.setAttribute("msg", "文件大于5M,上传失败!"); return mapping.findForward("fail"); } FileOutputStream fos = new FileOutputStream("d:\" + uaf.getMyfile().getFileName()); fos.write(uaf.getMyfile().getFileData()); fos.flush(); fos.close(); return mapping.findForward("upload"); } catch (Exception e) { request.setAttribute("msg", " 抱歉,上传出错,请稍后再试。"); return mapping.findForward("fail"); } } }
当然,今天不是申诉struts,也更没资格批评人家。相反,我始终觉得,作为开源框架,struts做得已经相当稳定相当优秀的了,特别是在MVC模式的开发上,可以说是提供了一个非常标准的榜样。但,正所谓“金无足赤,人无完人”,开源框架正需要我们所有人去努力更新完善。
最后
以上就是单纯书本最近收集整理的关于关于文件上传,我要向struts提点意见的全部内容,更多相关关于文件上传内容请搜索靠谱客的其他文章。
发表评论 取消回复