Java servlet 下载文件名特殊字符
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5325322/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java servlet download filename special characters
提问by jabal
I am writing a simple file download servlet and I can't get correct filenames. Tried URLEncoding and MimeEncoding the filename as seen in existing answers, but none of them worked.
我正在编写一个简单的文件下载 servlet,但无法获得正确的文件名。尝试 URLEncoding 和 MimeEncoding 现有答案中看到的文件名,但它们都不起作用。
The fileData object in the following snippet contains the mime type, the byte[] content and the filename, that needs at least ISO-8859-2 charset, ISO-8859-1 is not enough.
以下代码段中的 fileData 对象包含 mime 类型、byte[] 内容和文件名,至少需要 ISO-8859-2 字符集,ISO-8859-1 是不够的。
How can I get my browser to display the downloaded filename correctly?
如何让浏览器正确显示下载的文件名?
Here is an example of the filename: árvízt?r?tük?rfúrógép.xls and it results in: árvíztqrptük?rfúrógép.xls
这是文件名的示例:á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();
}
回答by sporak
I found out solution that works in all browsers I have installed (IE8, FF16, Opera12, Chrome22).
It's based on the fact, that browsers expect value in filename parameter, that is encoded in browsers native encoding, if no [different] encoding is specified.
我找到了适用于我安装的所有浏览器(IE8、FF16、Opera12、Chrome22)的解决方案。
它基于这样一个事实,即浏览器期望文件名参数中的值,如果未指定 [不同] 编码,则该参数以浏览器本机编码进行编码。
Usually browser's native encoding is utf-8 (FireFox, Opera, Chrome). But IE's native encoding is Win-1250.
通常浏览器的原生编码是 utf-8(FireFox、Opera、Chrome)。但是 IE 的原生编码是 Win-1250。
So if we put value into filename parametr, that is encoded by utf-8/win-1250 according to user's browser, it should work. At least, it works for me.
因此,如果我们将 value 放入 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 ...
}
Of course, this is tested only on browsers mentioned above and I cannot guarante on 100% that this will work in any browser all time.
当然,这仅在上面提到的浏览器上进行了测试,我不能 100% 保证这将在任何浏览器中始终有效。
Note #1 (@fallen): It's not correct to use URLEncoder.encode() method. Despite method's name, it doesn't encode string into URL-encoding, but it does encode into form-encoding. (Form-encoding is quite similiar to URL-encoding and in a lot of cases it produces same results. But there are some differences. For example space character ' ' is encoded different: '+' instead of '%20')
Note #1 (@fallen):使用 URLEncoder.encode() 方法是不正确的。尽管方法的名称,它不会将字符串编码为 URL 编码,但它会编码为表单编码。(表单编码与 URL 编码非常相似,在很多情况下它会产生相同的结果。但有一些差异。例如,空格字符 ' ' 的编码不同:'+' 而不是 '%20')
For correct URL-encoded string you should use URI class:
对于正确的 URL 编码字符串,您应该使用 URI 类:
URI uri = new URI(null, null, "árvízt?r?tük?rfúrógép.xls", null);
System.out.println(uri.toASCIIString());
回答by ilalex
回答by Michael-O
Based on the great answers given here, I have developed an extended version which I have put into production already. Based on RFC 5987and thistest suite.
基于这里给出的很好的答案,我开发了一个扩展版本,我已经投入生产了。基于RFC 5987和这个测试套件。
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());
}
}
回答by fallen
I have recently solved this issue in my application. here is the solution for firefox only, it sadly fails on IE.
我最近在我的应用程序中解决了这个问题。这是仅适用于 Firefox 的解决方案,遗憾的是它在 IE 上失败了。
response.addHeader("Content-Disposition", "attachment; filename*='UTF-8'" + URLEncoder.encode("árvízt?r?tük?rfúrógép", "UTF-8") + ".xls");
response.addHeader("Content-Disposition", "attachment; filename*='UTF-8'" + URLEncoder.encode("árvízt?r?tük?rfúrógép", "UTF-8") + ".xls");
回答by reznic
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);
}
}
回答by Valerii Starovoitov
Summing all I read so far this works for me:
总结我到目前为止阅读的所有内容,这对我有用:
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);