Java сервлет скачать имя файла специальные символы - PullRequest
10 голосов
/ 16 марта 2011

Я пишу простой сервлет загрузки файлов и не могу получить правильные имена файлов.Пробовал URLEncoding и MimeEncoding имени файла, как видно из существующих ответов, но ни один из них не работал.

Объект fileData в следующем фрагменте содержит тип mime, содержимое byte [] и имя файла, для которого требуется как минимум ISO-8859-2 charset, 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();
  }

Ответы [ 6 ]

20 голосов
/ 13 ноября 2012

Я нашел решение, которое работает во всех установленных мной браузерах (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());
3 голосов
/ 26 мая 2015

Основываясь на великолепных ответах, приведенных здесь, я разработал расширенную версию, которую уже внедрил в производство.На основе 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());
    }

}
3 голосов
/ 16 марта 2011

К сожалению, это зависит от браузера.См. эту тему обсуждения этой проблемы.Чтобы решить вашу проблему, посмотрите этот сайт с примерами различных заголовков и их поведения в различных браузерах.

1 голос
/ 05 апреля 2011

Я недавно решил эту проблему в своем приложении.Вот решение только для Firefox, к сожалению, оно не работает в IE.

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

0 голосов
/ 01 сентября 2017

Подводя итог всему, что я прочитал, это работает для меня:

    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);

0 голосов
/ 09 ноября 2016
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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...