Java сервлет скачать имя файла специальные символы
Я пишу простой сервлет загрузки файлов, и я не могу получить правильные имена файлов. Пробовал URLEncoding и MimeEncoding имя файла, как видно из существующих ответов, но ни один из них не работал.
Объект 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();
}
Ответы
Ответ 1
Я нашел решение, которое работает во всех браузерах, которые я установил (IE8, FF16, Opera12, Chrome22).
Это основано на том факте, что браузеры ожидают значения параметра filename, который кодируется в собственной кодировке браузеров, если не указано [различная] кодировка.
Обычно исходная кодировка браузера - utf-8 (FireFox, Opera, Chrome). Но исходная кодировка IE - Win-1250.
Итак, если мы поместим значение в filename parametr, которое закодировано 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());
Ответ 2
К сожалению, это зависит от браузера. См. эту тему обсуждения этой проблемы. Чтобы решить вашу проблему, просмотрите этот сайт с примерами разных заголовков и их поведением в разных браузерах.
Ответ 3
Основываясь на замечательных ответах, приведенных здесь, я разработал расширенную версию, которую я уже ввел в производство. На основе 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());
}
}
Ответ 4
Недавно я решил эту проблему в своем приложении.
вот решение только для firefox, оно, к сожалению, терпит неудачу в IE.
response.addHeader( "Content-Disposition", "attachment; filename * = 'UTF-8'" + URLEncoder.encode( "árvíztűrőtükörfúrógép", "UTF-8" ) + ".xls" );
Ответ 5
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);
}
}
Ответ 6
Суммируя все, что я прочитал до сих пор, это работает для меня:
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);