Servlet文件上传文件名编码

19

我正在使用Apache Commons Fileupload工具进行标准文件上传。我的问题是,如果上传的文件名包含特殊字符(如á,é,ú等),我无法获取正确的文件名。它们都会被转换为“?”号。

request.getCharacterEncoding() 显示为UTF-8,但我从字符串fileItem.getName()中获取的字节对于所有特殊字符都是相同的。

你能帮我看看问题出在哪里吗?

(一些细节:在Windows上使用Firefox 3.6.12、Weblogic 10.3)

这是我的代码片段:

 public CommandMsg(HttpServletRequest request) {
    Enumeration names = null;
    if (isMultipart(request)) {
      FileItemFactory factory = new DiskFileItemFactory();
      ServletFileUpload upload = new ServletFileUpload(factory);
      try {
        List uploadedItems = upload.parseRequest(request);
        Iterator i = uploadedItems.iterator();
        FileItem fileItem = null;
        while (i.hasNext()) {
          fileItem = (FileItem) i.next();
          if (fileItem.isFormField()) {
            // System.out.println("isFormField");
            setAttribute(fileItem.getFieldName(), fileItem.getString());
          } else {
            String enc = "utf-8";
            enc = request.getCharacterEncoding();
            String fileName = fileItem.getName();
            byte[] fnb = fileItem.getName().getBytes();
            byte[] fnb2 = null;
            try {
                fnb2 = fileItem.getName().getBytes(enc);
                String t1 = new String(fnb);
                String t2 = new String(fnb2);
                String t3 = new String(fnb, enc);
                String t4 = new String(fnb2, enc);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            setAttribute(fileItem.getFieldName(), fileItem);
          }
        }
      } catch (FileUploadException ex) {
        ex.printStackTrace();
      }

// etc..

你尝试过获取文件名并解码文件名吗?在一个广泛使用的库中出现这样的问题有点奇怪。 - gigadot
是的,正如您所看到的第一次尝试是:“String fileName = fileItem.getName();”,这很糟糕。所有其他行(t1..4)都只是孤注一掷的尝试.. :-) - jabal
4个回答

18

我曾经遇到同样的问题,并且像这样解决了它。

ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8"); 

FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext()) {
    FileItemStream item = iter.next();
    String name = item.getFieldName();
    InputStream stream = item.openStream();
    if (item.isFormField()) {
        String value = Streams.asString(stream, "UTF-8");
    } 
}
如果您的代码是基于http://commons.apache.org/fileupload/streaming.html提供的示例,则需要确保在上述两个位置设置UTF-8。

Christoph,你让我开心了。 写这样一个样板代码真是太可惜了,但它确实很好用:我花了半天时间在 HTML 部分寻找问题,而“问题”实际上出现在服务器端... ;) - f-renout
8
您不需要显式处理流,您可以直接使用FileItem#getString(String)方法,并在其中指定编码,例如:"UTF-8":item.getString("UTF-8") - Svante

2
你需要确保你要打印/写入/插入文件名的目标控制台/文件/数据库/任何地方也支持UTF-8。问号表示它没有配置为接受UTF-8,并且目标本身知道这一点。否则,你只会看到 乱码

由于问题中缺少有关目标的详细信息,我除了建议你阅读这篇文章以了解字符背后发生了什么之外,无法做更多事情。


你说得对,我没有提供有关显示目标的信息。在Eclipse的变量视图中调试时(我的程序中所有特殊字符都可以),我看到了问号,还在log4j日志文件、插入这些名称的数据库以及将文件下载回客户端时看到了问号。 - jabal
我总是通过调试来解决这类问题。如果我可以在监视窗口中看到正确的字符串,那么追踪它们出错的地方就很简单了。然而,在这种情况下,当我第一次获取文件名时,它是不正确的。 - jabal
据我了解,浏览器会在HTTP头中告知消息的编码方式。当请求被解析时,应该使用这个编码方式。Apache javadoc中也提到了ServletFileUpload.setHeaderEncoding的用法:“如果未指定或为空,则使用请求的编码方式。”在我的情况下,enc = request.getCharacterEncoding() 的结果是“UTF-8”,所以我认为这就是浏览器发送的编码方式。但为什么解析器无法正确获取文件名呢..?:-( - jabal
从更改Eclipse工作区编码开始:Window > Preferences > General > WorkspaceText file encoding 设置为UTF-8。 - BalusC

2
通过显式调用ServletFileUpload实例的.setHeaderEncoding("ISO-8858-2")方法解决了该问题。

1
请使用ISO-8859-2而不是Java不支持的ISO-8858-2:http://docs.oracle.com/javase/7/docs/technotes/guides/intl/encoding.doc.html;当使用ISO-8858-2编码时,我们遇到了java.io.UnsupportedEncodingException: ISO-8858-2。DiskFileUpload upload = new DiskFileUpload(); upload.setHeaderEncoding("ISO-8859-2") - Ahmed MANSOUR

0

对于这些特殊字符,您可以将编码设置为“iso 8859-1”。UTF-8似乎无法正常工作。

如果您没有设置任何编码类型,则Linux机器将采用默认编码UTF-8,而Windows将采用兼容编码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接