2011-03-16 35 views
10

Estoy escribiendo un servlet de descarga de archivos simple y no puedo obtener los nombres correctos de los archivos. Intentó URLEncoding y MimeEncoding el nombre del archivo como se ve en las respuestas existentes, pero ninguno de ellos funcionó.Java servlet download nombre de archivo caracteres especiales

El objeto fileData en el siguiente fragmento contiene el tipo de mimo, el contenido de bytes [] y el nombre de archivo, que necesita al menos un juego de caracteres ISO-8859-2, ISO-8859-1 no es suficiente.

¿Cómo puedo hacer para que mi navegador muestre correctamente el nombre del archivo descargado?

Aquí se muestra un ejemplo del nombre de archivo: árvíztűrőtükörfúrógép.xls y el resultado es: á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

favor dar algunos ejemplos de cómo se ven los nombres de archivos y lo que se obtiene en su lugar. – BalusC

+0

árvíztűrőtükörfúrógép.xls -> árvíztqrptükörfúrógép.xls – jabal

+1

Sí, tienes razón. Estos dos caracteres no están en ISO-8859-1 solo en ISO-8859-2, causando muchos problemas para cada desarrollador húngaro ... :-) – jabal

Respuesta

20

Encontré una solución que funciona en todos los navegadores que tengo instalados (IE8, FF16, Opera12, Chrome22).
Se basa en el hecho de que los navegadores esperan un valor en el parámetro de nombre de archivo codificado en la codificación nativa de los navegadores, si no se especifica una codificación [diferente].

Por lo general, la codificación nativa del navegador es utf-8 (FireFox, Opera, Chrome). Pero la codificación nativa de IE es Win-1250.

Así que si ponemos valor en el nombre del archivo parametr, que está codificado por utf-8/win-1250 según el navegador del usuario, debería funcionar. Al menos, funciona para mí.

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 ... 
} 

Por supuesto, esto se prueba sólo en los navegadores mencionados anteriormente y no puedo guarante en el 100% de que esto funcionará en cualquier navegador de todos los tiempos.

Nota n. ° 1 (@fallen): No es correcto utilizar el método URLEncoder.encode(). A pesar del nombre del método, no codifica cadena en codificación URL, pero sí codifica en codificación de formulario. (La codificación de formulario es bastante similar a la codificación URL y en muchos casos produce los mismos resultados. Pero hay algunas diferencias. Por ejemplo, el carácter espacial '' está codificado diferente: '+' en lugar de '% 20')

Para la correcta clase URI URL codificada cadena que se debe utilizar:

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

Creo que seguirás teniendo problemas si tu nombre de archivo contiene "pero, de lo contrario, es increíble, ¡gracias! – teedyay

+3

La codificación nativa de IE es la página de códigos de Europa Central y del Este? Lo único que demuestra es que IE usa el local la ubicación del sistema del navegador. Lamentablemente, no creo que haya una manera confiable de detectarlo desde el servidor. –

+1

¿Por qué funciona esto? Si el 'fileName' original es solo un carácter, por ejemplo' ő', entonces 'fileName. getBytes ("UTF-8") 'devolverá una matriz de bytes con dos elementos' 0xC5 0x91'. La solución anterior recorre estos dos bytes y los agrega a una nueva cadena. Esta nueva cadena tendrá dos * caracteres * de longitud y cuatro * bytes * largo. ¿Qué diablos? Por cierto, funciona, pero no puedo entender por qué. –

3

Por desgracia, depende del navegador. Consulte this tema de discusión este problema. Para resolver su problema, mire this site con ejemplos de diferentes encabezados y su comportamiento en diferentes navegadores.

1

Recientemente he resuelto este problema en mi aplicación. aquí está la solución solo para Firefox, lamentablemente falla en IE.

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

+0

gracias, pero todavía estoy buscando la solución definitiva ... :-) Actualmente Cambio cada ű a u y ő a o en nombres de archivos, ¿esto es mejor que? marcas. – jabal

+0

¿Alguien podría decirme cuál es el resultado en caso de que use Safari 5.1.7. Estoy teniendo los mismos problemas. el código anterior funciona bien en Firefox, Chrome y IE, pero no funciona en Safari. – vermaraj

3

sobre la base de las grandes respuestas que se dan aquí, he desarrollado una versión extendida que he puesto en la producción ya. Basado en RFC 5987 y this suite de pruebas.

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

mejor respuesta. gracias. – talipkorkmaz

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

Resumiendo todo lo leído hasta ahora esto funciona para mí:

 

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

Cuestiones relacionadas