关于文件上传,我要向struts提点意见

简介:

    前两天,公司一同事要做一个包含有文件上传的功能模块,问我采取哪种技术比较好。由于项目的技术架构是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
16
import  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,并未将异常情况带回到Actionstruts这种“极其不负责任”的行为直接导致了,开发人员不知道在什么时候告诉用户他传的文件过大了!即使可以在struts的配置文件中指定上传文件的最大值。更过分的是,对于上传过程中出现的其它问题,struts压根就没有去处理它,而是在自己的日志中记录了一下,就直接抛给了上一层了。  

   而对开发人员而言,有时候是一定要获得这些(甚至是全部)异常,该咋办呢?

   对不起,您只有自己定制MultipartRequestHandler了。由于时间的关系,我没有定制该类,在这里仅给大家提供个思路,希望朋友们,能够帮我完成并恳请与我交流。在写之前,您务必明白commons-fileupload上传控件在上传过程中到底可能抛出那些异常?那么在定制MultipartRequestHandler.handleRequest的时候,你使用try/catch尽可能的捕获所有异常,并放到request的attribute里去就哦了。完了之后,在struts-config.xml中使用<controller>标签指定让struts使用你的处理类。这样,我们再用FormFile上传文件,一旦上传过程中出现了异常,就会被写入requestattributs里。而在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模式的开发上,可以说是提供了一个非常标准的榜样。但,正所谓“金无足赤,人无完人”,开源框架正需要我们所有人去努力更新完善



     本文转自NightWolves 51CTO博客,原文链接:http://blog.51cto.com/yangfei520/1258965,如需转载请自行联系原作者






相关文章
|
2月前
|
存储 Java 数据库
学妹哭着找你教的Javaweb的文件上传与下载
该内容主要介绍了JavaWeb中的文件上传和下载操作。首先,通过一个JSP表单接收用户输入的图书信息和封面照片,表单数据提交给`AddBookServlet`。在Servlet中,使用SmartUpload库处理上传的图片,将图片以书号命名并保存至服务器,同时将图书信息存储到数据库。之后,请求转发到`ShowBook.jsp`展示所添加图书的详细信息。文件下载部分,通过`downServlet`完成,用户点击链接触发下载,Servlet设置响应头以附件形式提供文件下载。
40 1
|
11月前
|
前端开发
书城项目全部代码4
书城项目全部代码4
40 0
|
2月前
|
数据采集 开发框架 搜索推荐
开题报告-基于SpringBoot的“遇见”婚恋交友平台的设计与实现
开题报告-基于SpringBoot的“遇见”婚恋交友平台的设计与实现
306 0
|
11月前
书城项目全部代码3
书城项目全部代码3
71 0
|
11月前
|
数据安全/隐私保护
书城项目全部代码1
书城项目全部代码1
51 0
|
11月前
书城项目全部代码2
书城项目全部代码2
33 0
|
11月前
|
Java Docker 容器
这份神仙版的SpringBoot学习文档,简直把所有操作都给写出来了
2023年秋招了,各路码友们都开始磨拳擦脚,背面试题、知识点。小编最近得一良友赠送了一份关于SpringBoot的学习笔记,简直不要好用,理论解析言简意赅,每一步操作都有图片展示。这么好的东西肯定不能私藏,我现在将这份笔记赠送给大家,祝大家前程似锦,Offer不断!
|
前端开发 JavaScript 容器
开心档 - 软件开发入门之 Bootstrap4 表单
在本章中,我们将学习如何使用 Bootstrap 创建表单。Bootstrap 通过一些简单的 HTML 标签和扩展的类即可创建出不同样式的表单。
开心档 - 软件开发入门之 Bootstrap4 自定义表单
Bootstrap4 自定义表单Bootstrap4 可以自定义一些表单的样式来替换浏览器默认的样式。
|
存储 缓存 JavaScript
想好怎么学 Servlet规范了嘛?想好了嘛?没想好先看看这篇文章(爆肝之作),先看着然后慢慢想!!
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。 狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
想好怎么学 Servlet规范了嘛?想好了嘛?没想好先看看这篇文章(爆肝之作),先看着然后慢慢想!!