在调试struts2文件下载时,提示错误如下:
java.lang.IllegalArgumentException: Can not find a java.io.InputStream with the name [fileStream] in the invocation stack. Check the <param name="inputName"> tag specified for this action. org.apache.struts2.dispatcher.StreamResult.doExecute(StreamResult.java:237) org.apache.struts2.dispatcher.StrutsResultSupport.execute(StrutsResultSupport.java:186) com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:373) com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:277)
出错误,然后根据错误提示,寻找原因。根据错误栈式顺序,出现在第一行的往往是错误源。
请看第一句,想必大家都能看懂吧,找不到fileStream,这个是inputName属性的值,提示让我检查inputName参数是否符合action。
根据错误,一直在提示inputName参数,然后把怀疑重心全放到struts配置文件中。
首先来看一下文件下载的程序:
在struts2中是以InputStream的形式返回给系统,客户端是直接访问action,然后action以InputStream的形式返回给系统,然后系统生成文件的http响应的消息头。
文件下载的action
package com.download; import java.io.InputStream; import java.io.UnsupportedEncodingException; import javax.servlet.http.HttpServletResponse; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; /* * 文件下载 */ public class DownloadFile extends ActionSupport { // private InputStream inputStream; private String fileName; public String getFileName() { return fileName; } public void setFileName(String fileName) { try { this.fileName = fileName; } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public InputStream getFileStream() { return ServletActionContext.getServletContext().getResourceAsStream("uploadFile\\"+fileName); } //只有返回return时,才会用到result,然后才会用到result中的参数,即fileInput, //然后才会调用getFileInput方法,对fileInput进行赋值 public String execute() throws Exception { return "success"; } }
Struts中的配置文件:
<action name="download" class="com.download.DownloadFile"> <result name="success" type="stream"> <param name="inputName">fileStream</param> <param name="contentType">application/octet-streams</param> <param name="contentDisposition">attachment;filename="${fileName}"</param> </result> </action>
来解释一下配置中下载文件的标签。
inputName:下载文件输出流,文件以流的形式返回。所以action中可以只用到get方式,set方式可以省略。
contentType:文件下载的类型。其实就是MIME类型。如: image/gif(gif格式),application/x-javascript(js格式)
contentDisposition:下载文件的处理方式。内联(inline)和附件(attachment)两种方式。是以附件的形式保存(attachment),还是直接在浏览器中显示(inline)。
attachment;filename="${fileName}"这种方式,是以附件形式保存。另一种:filename="${fileName}"则说明是直接在浏览器中显示。
另外还有一个参数,上述配置文件中没有配置:bufferSize下载文件的缓冲大小。
Jsp界面:
<body> <a href="download?fileName=Struts2多个文件上传示例.doc">Struts2多个文件上传示例.doc</a> </body>
当时一直用中文名的文件做测试,一次用英文做测试,结果成功了,才发现原来是文件中文名的原因。
原来struts中的配置文件是没有错误的,原来错误提示误导我,一直在寻找配置文件的原因。
中文乱码问题,这个问题,对于IT人士并不陌生。
分析:
本jsp中有UTF-8进行编码,所以提交请求的url中,完全转换成了UTF-8,此时的提交的fileName也编码成了???.txt(若是上传文本格式的),在某一路径下寻找匹配的文件,当然会找不到。因此返回的文件流为null。
解决的办法如下:
提交的请求文件名是用UTF-8进行编码,然后在action用UTF-8进行解码文件名。
代码如下:
publicvoid setFileName(String fileName) { try { this.fileName = new String(fileName.getBytes("ISO-8859-1"),"UTF-8"); System.out.println(fileName); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
这种办法就可以解决文件名是中文了。
若是按照以上这种方式写的话,确实能实现,并且能够成功下载。但是仍然会有一个问题。因为下载保存的时候不会保存中相应的类型,我们不知道要保存什么类型,当然也不知道用什么程序打开。
那如何解决呢?办法如下:在系统返回文件生成响应头信息,更改响应头信息。代码如下:
public InputStream getFileStream() { HttpServletResponse response = ServletActionContext.getResponse(); try { response.setHeader("Content-Disposition", "attachment;fileName=" + java.net.URLEncoder.encode(fileName,"UTF-8")); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return ServletActionContext.getServletContext().getResourceAsStream("uploadFile\\"+fileName); }
这样就OK了,即解决了中文文件名,又解决了保存相应类型的问题。
看到这种效果,心里才会挺舒服的。
但是,我还有一个疑问,请教路过的人解答。
在上述setFileName方法中,已经用UTF-8格式进行解码了。恢复到提交的中文参数文件名。但是为啥输出的结果仍然是???.doc呢?
this.fileName = new String(fileName.getBytes("ISO-8859-1"),"UTF-8"); System.out.println(fileName);