2011-03-16 33 views
10

我正在写一个简单的文件下载servlet,我无法获得正确的文件名。尝试URLEncoding和MimeEncoding在现有的答案中看到的文件名,但没有一个工作。Java servlet下载文件名特殊字符

以下片段中的fileData对象包含MIME类型,字节[]内容和文件名,至少需要ISO-8859-2字符集,ISO-8859-1是不够的。

如何让我的浏览器正确显示下载的文件名?

这里是文件名的例子:árvíztűrőtükörfúrógép.xls并且它导致:árvíztqrptükörfúrógép.xls

protected void renderMergedOutputModel(Map model, HttpServletRequest req, HttpServletResponse res) throws Exception { 

    RateDocument fileData = (RateDocument) model.get("command.retval"); 
    OutputStream out = res.getOutputStream(); 
    if(fileData != null) { 
     res.setContentType(fileData.getMime()); 
     String enc = "utf-8"; //tried also: ISO-8859-2 

     String encodedFileName = fileData.getName(); 
      // also tried URLencoding and mime encoding this filename without success 

     res.setCharacterEncoding(enc); //tried with and without this 
     res.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName); 
     res.setContentLength(fileData.getBody().length); 
     out.write(fileData.getBody()); 
    } else { 
     res.setContentType("text/html"); 
     out.write("<html><head></head><body>Error downloading file</body></html>" 
       .getBytes(res.getCharacterEncoding())); 
    } 
    out.flush(); 
    } 
+0

请给,你会得到什么,而不是文件名怎么看一些例子。 – BalusC 2011-03-16 12:53:55

+0

árvíztűrőtükörfúrógép.xls - >árvíztqrptükörfúrógép.xls – jabal 2011-03-16 13:15:49

+1

是的,你是对的。这两个字符在ISO-8859-2中仅在ISO-8859-2中不存在,导致每个匈牙利开发者都遇到许多问题.. :-) – jabal 2011-03-16 13:28:32

回答

20

我发现了适用于我安装的所有浏览器(IE8,FF16,Opera12,Chrome22)的解决方案。
这是基于这样一个事实,即如果没有指定[不同的]编码,浏览器期望在文件名参数中使用浏览器本机编码编码的值。

通常浏览器的本机编码是utf-8(FireFox,Opera,Chrome)。但IE的本机编码是Win-1250。

因此,如果我们将值放入filename参数中,那么根据用户的浏览器将其编码为utf-8/win-1250,它应该可以工作。至少,它适用于我。

String fileName = "árvíztűrőtükörfúrógép.xls"; 

String userAgent = request.getHeader("user-agent"); 
boolean isInternetExplorer = (userAgent.indexOf("MSIE") > -1); 

try { 
    byte[] fileNameBytes = fileName.getBytes((isInternetExplorer) ? ("windows-1250") : ("utf-8")); 
    String dispositionFileName = ""; 
    for (byte b: fileNameBytes) dispositionFileName += (char)(b & 0xff); 

    String disposition = "attachment; filename=\"" + dispositionFileName + "\""; 
    response.setHeader("Content-disposition", disposition); 
} catch(UnsupportedEncodingException ence) { 
    // ... handle exception ... 
} 

当然,这只在上面提到的浏览器上进行过测试,我无法保证100%这个功能可以在任何浏览器中工作。

注意#1(@fallen): 使用URLEncoder.encode()方法是不正确的。尽管方法的名称,它不会将字符串编码为URL编码,但它确实编码为表单编码。 (表单编码类似于URL编码,在很多情况下它会产生相同的结果,但是有一些差异,例如空格字符''编码不同:'+'而不是'%20')

对于正确的URL编码字符串,你应该使用URI类:

URI uri = new URI(null, null, "árvíztűrőtükörfúrógép.xls", null); 
System.out.println(uri.toASCIIString()); 
+0

我认为如果你的文件名中包含“但除此之外,这真是太棒了 - 谢谢!” – teedyay 2012-12-18 12:21:42

+3

IE的本地编码是中欧/东欧代码页?你一定是在开玩笑。它唯一显示的是IE使用本地浏览器的系统区域设置不幸的是,我不认为有一种可靠的方法可以从服务器上检测到它 – 2013-02-27 05:32:42

+1

为什么它能正常工作?如果最初的'fileName'只是一个单独的字符,例如'ő',那么'fileName。 getBytes(“UTF-8”)将返回一个包含两个元素“0xC5 0x91”的字节数组,上面的解决方案遍历这两个字节并将它们附加到一个新的字符串中,这个新的字符串将是两个*字符* *字节*长。到底是什么?顺便说一下它的工作原理,但我无法绕过我的头为什么 – 2015-06-02 13:42:35

3

不幸的是,它依赖于浏览器。请参阅this讨论这个问题的话题。要解决您的问题,请查看this site,其中包含不同标题的示例以及它们在不同浏览器中的行为。

1

我最近在我的应用程序中解决了这个问题。 这里是唯一的解决方案,它可悲的是在IE浏览器上失败。

response.addHeader(“Content-Disposition”,“attachment; filename * ='UTF-8'”+ URLEncoder.encode(“árvíztűrőtükörfúrógép”,“UTF-8”)+“.xls”);

+0

谢谢,但我仍在寻找终极解决方案.. :-)目前我将每个change更改为u和ő到o的文件名,这比哪个更好?分数。 – jabal 2011-05-10 08:09:15

+0

任何人都可以告诉我什么是我使用Safari 5.1.7的结果。我有同样的问题。上面的代码在firefox,chrome和IE浏览器上运行良好,但它不适用于safari。 – vermaraj 2014-07-31 09:45:43

3

基于这里给出的伟大的答案,我已经开发了我已经投入生产的扩展版本。基于RFC 5987this测试套件。

String filename = "freaky-multibyte-chars"; 
StringBuilder contentDisposition = new StringBuilder("attachment"); 
CharsetEncoder enc = StandardCharsets.US_ASCII.newEncoder(); 
boolean canEncode = enc.canEncode(filename); 
if (canEncode) { 
    contentDisposition.append("; filename=").append('"').append(filename).append('"'); 
} else { 
    enc.onMalformedInput(CodingErrorAction.IGNORE); 
    enc.onUnmappableCharacter(CodingErrorAction.IGNORE); 

    String normalizedFilename = Normalizer.normalize(filename, Form.NFKD); 
    CharBuffer cbuf = CharBuffer.wrap(normalizedFilename); 

    ByteBuffer bbuf; 
    try { 
     bbuf = enc.encode(cbuf); 
    } catch (CharacterCodingException e) { 
     bbuf = ByteBuffer.allocate(0); 
    } 

    String encodedFilename = new String(bbuf.array(), bbuf.position(), bbuf.limit(), 
      StandardCharsets.US_ASCII); 

    if (StringUtils.isNotEmpty(encodedFilename)) { 
     contentDisposition.append("; filename=").append('"').append(encodedFilename) 
       .append('"'); 
    } 

    URI uri; 
    try { 
     uri = new URI(null, null, filename, null); 
    } catch (URISyntaxException e) { 
     uri = null; 
    } 

    if (uri != null) { 
     contentDisposition.append("; filename*=UTF-8''").append(uri.toASCIIString()); 
    } 

} 
+0

最好的答案。 – talipkorkmaz 2015-07-06 14:01:54

0
private void setContentHeader(HttpServletResponse response, String userAgent, String fileName) throws UnsupportedEncodingException { 
    fileName = URLEncoder.encode(fileName, "UTF-8"); 
    boolean isFirefox = (userAgent.indexOf("Firefox") > -1); 
    if (isFirefox) { 
     response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + fileName); 
    } else { 
     response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName); 
    } 
} 
0

取所有我读到目前为止,这对我的作品:

 

    URI uri = new URI(null, null, fileName, null); 
    String fileNameEnc = uri.toASCIIString(); //URL encoded. 
    String contDisp = String.format("attachment; filename=\"%s\";filename*=utf-8''%s", fileName, fileNameEnc); 
    response.setHeader("Content-disposition", contDisp);