我正在编写一个简单的文件下载servlet,但我无法获得正确的文件名。试过URLEncoding和MimeEncoding文件名,如现有答案所示,但没有一个工作。
以下代码段中的fileData对象包含mime类型,byte []内容和文件名,至少需要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 :(得分:20)
我发现解决方案适用于我安装的所有浏览器(IE8,FF16,Opera12,Chrome22)。
它基于以下事实:如果没有指定[不同]编码,浏览器期望文件名参数中的值,这是在浏览器本机编码中编码的。
通常浏览器的原生编码是utf-8(FireFox,Opera,Chrome)。但IE的原生编码是Win-1250。
因此,如果我们将值放入文件名参数,根据用户的浏览器由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());
答案 1 :(得分:3)
答案 2 :(得分:3)
基于这里给出的优秀答案,我已经开发了一个已经投入生产的扩展版本。基于RFC 5987和this测试套件。
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());
}
}
答案 3 :(得分:1)
我最近在我的申请中解决了这个问题。 这里只是firefox的解决方案,它遗憾地在IE上失败了。
response.addHeader(“Content-Disposition”,“attachment; filename * ='UTF-8'”+ URLEncoder.encode(“árvíztűrőtükörfúrógép”,“UTF-8”)+“。xls”);
答案 4 :(得分: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);
}
}
答案 5 :(得分: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);