我的环境
英文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)里说的转一下,否则浏览器提示保存时是乱码