我的环境
英文windows xp
location :United states
language for non-Unicode program: Chinese(PRC)
tomcat 6.0
struts2-core-2.2.1
网上关于struts file upload/download的文章好多地雷。在我的环境中,花了一个星期才把struts2下上传下载都调好,以下是一点经验。
1)使用缺省的jakarta上传空文件会抛出NullPointerException,你可以自己捕捉FileNotFoundException然后自己给一个空的byte[],也可以换用其他的file upload component,比如cos。jakarta把request转存到本时会给一个uuid,cos则是存成request中的fileName。尽管每次处理完这个temp file都会被删掉,但大量并发时上传同名文件还是会有冲突(后一个覆盖前一个)
2)如果你想用cos的话,一定注意,google出来前几个配法都是不成功的 ,正确的配法是
2.1)写一个自己的cos->struts Adaptor (我也是网上找的)
package myapp.view.action.common; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts2.dispatcher.multipart.MultiPartRequest; import com.oreilly.servlet.multipart.FilePart; import com.oreilly.servlet.multipart.MultipartParser; import com.oreilly.servlet.multipart.ParamPart; import com.oreilly.servlet.multipart.Part; @SuppressWarnings("unchecked") public class CosMultiPartRequest implements MultiPartRequest { private static Log log = LogFactory.getLog(CosMultiPartRequest.class); private ArrayList<File> alFile; private Hashtable<String, ArrayList<?>> files; private ArrayList<String> fileNames; private ArrayList<String> contentType; private ArrayList<String> fileSystemName; private Hashtable<String, ArrayList<String>> parameters; private Hashtable<String, ArrayList<?>> filePara; private ArrayList<String> paraValues; private String encode; public CosMultiPartRequest() { super(); } public String getEncode() { return encode; } public void setEncode(String encode) { this.encode = encode; } public String[] getContentType(String arg0) { ArrayList<String> aList = (ArrayList<String>) files.get(arg0 + "_ContentType"); String[] str = new String[aList.size()]; for (int i = 0; i < aList.size(); i++) { str[i] = aList.get(i); } return str; } public List getErrors() { return Collections.EMPTY_LIST; } public File[] getFile(String arg0) { ArrayList<File> aList = (ArrayList<File>) files.get(arg0); File[] file = new File[aList.size()]; for (int i = 0; i < aList.size(); i++) { file[i] = aList.get(i); } return file; } public String[] getFileNames(String arg0) { ArrayList<String> aList = (ArrayList<String>) files.get(arg0 + "_name"); String[] stt = new String[aList.size()]; for (int i = 0; i < aList.size(); i++) { stt[i] = aList.get(i); } return stt; } public Enumeration<String> getFileParameterNames() { return filePara.keys(); } public String[] getFilesystemName(String arg0) { ArrayList<String> arrayList = (ArrayList<String>) files.get(arg0 + "_SysName"); String[] ss = new String[arrayList.size()]; for (int i = 0; i < arrayList.size(); i++) { ss[i] = arrayList.get(i); } return ss; } public String getParameter(String arg0) { if (parameters.keySet().contains(arg0)) return arg0; return ""; } public Enumeration<String> getParameterNames() { return parameters.keys(); } public String[] getParameterValues(String arg0) { ArrayList<String> al = parameters.get(arg0); String[] str = new String[al.size()]; for (int i = 0; i < al.size(); i++) { str[i] = al.get(i); } return str; } public void parse(HttpServletRequest request, String saveDir) throws IOException { if (log.isDebugEnabled()) { log.debug("Parse by CosMultiPartRequest"); } MultipartParser mp = new MultipartParser(request, 1024 * 1024 * 10); // actually ,should be injected mp.setEncoding("UTF-8"); Part part; files = new Hashtable<String, ArrayList<?>>(); alFile = new ArrayList<File>(); fileNames = new ArrayList<String>(); fileSystemName = new ArrayList<String>(); contentType = new ArrayList<String>(); filePara = new Hashtable<String, ArrayList<?>>(); paraValues = new ArrayList<String>(); parameters = new Hashtable<String, ArrayList<String>>(); for (int i = 0; (part = mp.readNextPart()) != null; i++) { if (part.isFile()) { FilePart fp = (FilePart) part; alFile = (ArrayList) files.get(fp.getName()); if (alFile == null || alFile.size() < 1) { alFile = new ArrayList(); } File fil = new File(saveDir + fp.getFileName()); fp.writeTo(fil); alFile.add(fil); fileNames = (ArrayList) files.get(fp.getName() + "_name"); if (fileNames == null || fileNames.size() < 1) { fileNames = new ArrayList<String>(); } fileNames.add(fp.getFileName()); fileSystemName = (ArrayList) files.get(fp.getName() + "_SysName"); if (fileSystemName == null || fileSystemName.size() < 1) { fileSystemName = new ArrayList<String>(); } fileSystemName.add(fp.getFilePath()); contentType = (ArrayList) files.get(fp.getName() + "_ContentType"); if (contentType == null || contentType.size() < 1) { contentType = new ArrayList<String>(); } contentType.add(fp.getContentType()); files.put(fp.getName(), alFile); files.put(fp.getName() + "_name", fileNames); files.put(fp.getName() + "_SysName", fileSystemName); files.put(fp.getName() + "_ContentType", contentType); filePara.put(fp.getName(), alFile); } else if (part.isParam()) { ParamPart pp = (ParamPart) part; paraValues = parameters.get(pp.getName()); if (paraValues == null || paraValues.size() < 1) { paraValues = new ArrayList<String>(); } paraValues.add(pp.getStringValue()); parameters.put(pp.getName(), paraValues); } else { } } }
2.2) 在struts.xml中配一个叫cos的bean,把自己写的这个Adaptor配到系统中
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" class="myapp.view.action.common.CosMultiPartRequest" name="cos" scope="prototype">
网上很多文章都没提到,struts-default.xml里面是没配cos这个bean的。
2.3) 打开 struts中的开关,同样是在struts.xml中
<constant name="struts.multipart.handler " value="cos" />
这里不要 像网上写的那样配成struts.multipart.parser , 那个属性不起作用的
3) 上传时,最好把contentype,fileName用数据库单独存起来,这些信息下载时都要用到。至于内容是存在数据库还是文件系统中随意
4) 上传时,文件名为乱码(不是%BC这样的Unicode编码,而是 饭 这样的乱码),这是由于上传(包含File控件)的jsp没有指定编码。把jsp的meta改成
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
5)下载时,遇见Can not find a java.io.InputStream with the name [XXX] in the invocation stack。这个异常很误导。可能不是你的param配错了,而是文件找不到,getXXX返回了null。最好在action里处理下,这时直接return INPUT算了。
6)下载时,链接中文件名中显示为乱码问题的方法,在tomcat/conf/server.xml中加Encoding参数 ,如下
<Connector connectionTimeout="20000" port="8081" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>
这似乎是个bug,见http://www.blogjava.net/wangzhouyu/archive/2007/04/26/113705.html ,原因嘛,是Tomcat对于get param(archor属于get method)没用ISO-8859-1去decode,而是自己搞了一套。很奇怪为什么这个bug现在还没fix
7)下载时,点击链接后本地浏览器显示的文件名为乱码,需要这样转一下
String downloadfileName = new String(fileName.getBytes("GBK"),"ISO-8859-1");
前面的GBK是怎么来的我也不知道,我只知道后面的"ISO-8859-1"是因为浏览器对fileName按照ISO-8859-1去解析, 可以参见这个文档http://www.busfly.net/csdn/post/449.html 。
但把GBK hardcoding 在这里就比较urgly了希望知道的同志指点一下这个GBK的来源
8)这些中文log下来可能也是编码,需要把log4j.xml的编码改一下,在FILE appender中加一行
<param name="encoding" value="UTF-8" />
9)下载时,为了让浏览器正确识别文件类型,刚才让存在数据库的那些contentType和Name就派上用场了
<result name="success" type="stream"> <param name="inputName">fileStream</param> <param name="contentType">{$attachment.contentType};</param> <param name="contentDisposition">attachment;filename="${downloadFileName}"</param> <param name="contentCharSet">UTF-8</param> </result>
这里的downloadFileName别忘了按照7)里说的转一下,否则浏览器提示保存时是乱码