【原创】Struts2学习笔记之文件的上传与下载
1. Struts2的文件上传需要Apache的commons-io-Version.jar和commons-fileupload-Version.jar两个jar包。
2. 页面中的<s:file name="example"/>标签会在相应的action类中寻找并执行三个setter方法:setExample,setExampleFileName和setExampleContentType。其中setExampleFileName方法将用户上传的文件的文件名(String,不包括文件路径)赋给action类的相应属性,setExampleContentType方法则对应上传文件的MIME类型(String),如"text/plain"。
3. 对上传文件最主要的处理就是完成文件的拷贝,具体做法是使用文件字节输出流将用户上传的文件写到某个目录下。
4. Struts2上传文件需要使用fileUpload拦截器,这个拦截器包含在defaultStack拦截器栈里,相应的拦截器类为org.apache.struts2.interceptor.FileUploadInterceptor。该拦截器有三个常用属性:maximumSize,allowedExtensions和allowedTypes,分别代表允许上传文件的大小的最大值,允许上传的文件后缀名和允许上传的文件MIME类型,多个项之间用逗号分隔。
注意:手动在<interceptor-ref>元素中配置fileupload拦截器后不要忘记紧接着添加defaultStack拦截器栈,因为此时defaultStack拦截器栈将不会被容器自动引用,必须手动添加上。
5. struts.multipart.saveDir用于指定存放临时文件的文件夹,该配置写在struts.properties文件中。可以使用struts.xml文件中的constant元素来覆盖默认的位置,例如:
<constant name="struts.multipart.saveDir" value="C:\"></constant>
上面这行代码指定了C盘根目录为临时文件夹。
6. 通过重写国际资源文件中struts.messages.error.content.type.not.allowed键和struts.messages.error.file.too.large键对应的值,可以实现自定义出错信息。例:
struts.xml文件:
<!-- 在struts.xml文件中使用constant元素指定在全局的message.properties文件中自定义出错信息 --> <constant name="struts.custom.i18n.resources" value="message"></constant>
message.properties文件:
<!-- 自定义上传文件类型不允许时的提示信息--> struts.messages.error.content.type.not.allowed = Error: the file type is not allowed, please try again <!-- 自定义上传文件过大时的提示信息--> struts.messages.error.file.too.large = Error: the file is too large, please try again
注意:message.properties文件应该位于WEB-INF/classes目录下。
7. 上传多个文件时,只要将相关action类中的属性变为数组类型或者List类型即可,struts会自动用文件上传页面表单中的信息填充。但要注意表单中多个file标签的name必须相同。若上传页面如下:
<!-- 上传多个文件时,file标签的name属性必须一致--> file1:<s:file name="file"> file2:<s:file name="file"> file3:<s:file name="file">
相应的action类如下:
//上传多个文件时对应的动作类 public class UploadAction extends ActionSupport { private List<File> file; .... }
则struts会自动将上传的三个文件都放入action类的成员变量file中,因为上传页面中三个file标签的name属性都是file!
8. Struts中的文件下载非常便捷,只需编写一个相应的action即可,提供一个返回InputStream流的方法,该输入流代表了被下载文件的入口,这个方法用来给被下载的数据提供输入流,意思是从这个流读出来,再写到浏览器那边供下载。这个方法由开发人员编写,只需要返回值为InputStream即可。
9. 文件下载的结果类型为stream,相应的类为org.apache.struts2.dispatcher.StreamResult。该结果类型有三个常用属性:contentType,contentDisposition和inputName。
contentType表示被下载文件的MIME类型,默认为text/plain。
contentDisposition表示下载文件的处理方式,有inline(默认)和attachment两种。若指定为inline模式,浏览器会先尝试直接打开文件,无法直接打开则弹出保存对话框;attachment模式则直接弹出文件保存对话框。语法格式为:<param>attachment;filename="example.txt"</param>。这时,无论服务器端真真被下载的文件名字是什么,文件保存对话框中显示的文件名都是example.txt。
inputName表示下载文件的来源流。若inputName取值为downloadFile,则在相应的action类中必须有一个返回类型为InputStream的getDownloadFile方法来返回下载的文件入口,参考第8点。
result配置实例:
<result name="success" type="stream">
<param name="contentType">text/plain</param>
<param name="contentDisposition">attachment;filename="example.txt"</param>
<param name="inputName">downloadFile</param>
</result>
10. 如果是未知的文件类型,或者说出现了浏览器不能打开的文件,例如.bean文件,或者说这个action是用来做动态文件下载的,事先并不知道未来的文件类型是什么,那么我们可以把contentType的值设置成为:application/octet-stream;charset=ISO8859-1 ,注意一定要加入charset,否则某些时候会导致下载的文件出错;有人说这时也可以设置成为application/x-download,根据笔者的实践,这个头也能正常工作,然而个别时候会出现浏览器无法识别的问题。
11. 文件名中包含中文的处理方法:
利用String nameInCN = String(“struts2中文.txt”.getBytes(), "ISO8859-1");的办法可以将struts2中文.txt转换为一个浏览器支持的字符串nameInCN,然后将这个字符串放置到<param>attachment;filename="..."</param>中区就可以了。
具体的操作方法可以参考这篇文章:Struts 2中实现文件下载(修正中文问题)
掌握了这个原理后,不难提出一些较上面那篇文章中所述更为简单的方法。下面是我自己想出的一个方法:
首先,依然要在相应action类中增加一个String类型的成员变量表示需要显示的文件名,这里不妨命名为filenameInCN。然后别忘了生成它的setter和getter方法。
其次,在struts.xml文件中配置相应的action的时候,向action传递一个参数,用以指定你希望显示给客户的文件名,当然,这个文件名是可以包含中文的,我们接下来要做的就是将这个包含中文的文件名做一些修改使浏览器可以显示它。这里我们结社指定的文件名为“中文.txt”具体配置请参考下面的代码。
接下来,修改action类中的setFilenameInCN方法,在这个方法里,我们使用上文所提及的办法将从配置文件中传过来的包含中文的文件名转换为浏览器可以识别的字符串。
最后,在配置文件的result元素中设置contentDisposition属性为attachment;filename="${filenameInCN}"即可。
好了,大功告成!现在用户在下载文件的时候,看到的就不再是一对乱码了,而是“中文.txt”。下面是上文所涉及的一些代码。
struts.xml文件中配置action,这里action的名字叫download:
<action name="download" class="com.test.action.DownloadAction"> <param name="filenameInCN">中文.txt</param> <result name="success" type="stream"> <param name="contentType">text/plain</param> <param name="contentDisposition">attachment;filename="${filenameInCN}"</param> <param name="inputName">downloadFile</param> </result> </action>
下面是相应的DownloadAction类,这里我们可以看到,被下载的文件实际上是服务器端WebRoot/abc目录下的bls.txt文件,但由于我们在action配置中指定了文件名为“中文.txt”并做了相应的处理,所以用户在下载的时候看到的文件名就是“中文.txt”,而不是bls.txt,参考第9点。
import java.io.InputStream; import com.opensymphony.xwork2.ActionSupport; import org.apache.struts2.ServletActionContext; public class DownloadAction extends ActionSupport { private String filenameInCN; public String getFilenameInCN() { return filenameInCN; } public void setFilenameInCN(String filenameInCN) { try { this.filenameInCN = new String(filenameInCN.getBytes(), "ISO-8859-1"); } catch (Exception e) { e.printStackTrace(); } } public InputStream getDownloadFile() { return ServletActionContext.getServletContext().getResourceAsStream("/upload/bls.txt"); } @Override public String execute() throws Exception { return SUCCESS; } }
参考资料: