2012-07-02 8 views
6

Tengo un código heredado al que me refiero (así que no puedo usar una URL con un componente de nombre de archivo codificado) que permita al usuario descarga un archivo desde nuestro sitio web. Dado que nuestros nombres de archivo a menudo están en muchos idiomas diferentes, todos están almacenados como UTF-8. Escribí un código para manejar la conversión de RFC5987 a un parámetro de nombre de archivo * adecuado. Esto funciona muy bien hasta que tenga un nombre de archivo con caracteres no ASCII y espacios. Por RFC, el carácter de espacio no es parte de attr_char por lo que se codifica como% 20. Tengo nuevas versiones de Chrome y de Firefox, y todas se están convirtiendo a% 20 en + en la descarga. Intenté no codificar el espacio y poner el nombre de archivo codificado entre comillas y obtener el mismo resultado. He olfateado la respuesta proveniente del servidor para verificar que el contenedor del servlet no se estaba volcando con mis encabezados y me parecen correctos. El RFC incluso tiene ejemplos que contienen% 20. ¿Me estoy perdiendo algo, o todos estos navegadores tienen un error relacionado con esto?manejo de nombre de archivo * parámetros con espacios a través de RFC 5987 resultados en '+' en nombres de archivo

Muchas gracias de antemano. El código que uso para codificar el nombre de archivo está debajo.

Peter

public static boolean bcsrch(final char[] chars, final char c) { 
    final int len = chars.length; 
    int base = 0; 
    int last = len - 1; /* Last element in table */ 
    int p; 

    while (last >= base) { 
     p = base + ((last - base) >> 1); 

     if (c == chars[p]) 
      return true; /* Key found */ 
     else if (c < chars[p]) 
      last = p - 1; 
     else 
      base = p + 1; 
    } 

    return false; /* Key not found */ 
} 

public static String rfc5987_encode(final String s) { 
    final int len = s.length(); 
    final StringBuilder sb = new StringBuilder(len << 1); 
    final char[] digits = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 
    final char[] attr_char = {'!','#','$','&','\'','+','-','.','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','^','_','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','|', '~'}; 
    for (int i = 0; i < len; ++i) { 
     final char c = s.charAt(i); 
     if (bcsrch(attr_char, c)) 
      sb.append(c); 
     else { 
      final char[] encoded = {'%', 0, 0}; 
      encoded[1] = digits[0x0f & (c >>> 4)]; 
      encoded[2] = digits[c & 0x0f]; 
      sb.append(encoded); 
     } 
    } 

    return sb.toString(); 
} 

actualización

Aquí está una captura de pantalla del diálogo de descarga que me pasa por un archivo con caracteres chinos con los espacios que se han mencionado en mi comentario.

screen cap of download dialog

+0

Aquí es una cabecera de la muestra que está causando este problema: Content- Disposición: archivo adjunto; filename * = UTF-8''Museum% 20% 5A% 69% 86.jpg –

+0

Ver http://greenbytes.de/tech/tc2231/#attwithquotedsemicolon - ese caso de prueba tiene un carácter de espacio en una cadena citada y aparece para trabajar en Firefox ¿Estamos probando cosas diferentes? –

+0

Eso se parece a algo más. Esa prueba comprueba el punto y coma dentro de una cadena entrecomillada. Mi problema es que tengo un nombre de archivo con caracteres chinos, así como espacios, así que estoy usando el formulario de nombre de archivo *, y en el token no se cita desde que leí algunos documentos que recomendaban no utilizar comillas con% escapes. Con el ejemplo de mi comentario anterior, los caracteres chinos se reconocen y convierten correctamente, pero el% 20 se asigna a +. –

Respuesta

10

Así como Julian señaló en los comentarios, que cometió un error de Java novato y se olvidó de hacer mi personaje a la conversión de bytes (por lo tanto I codificada punto de código del personaje en lugar de la representación de bytes del personaje), de ahí la la codificación fue completamente incorrecta. Esto se menciona claramente como un requisito en RFC 5987. Publicaré el código corregido para hacer la conversión. Una vez que la codificación es correcta, el navegador reconoce correctamente el parámetro filename * y el nombre de archivo utilizado para la descarga es correcto.

A continuación se muestra el código de escape corregido que opera en los bytes UTF-8 de la cadena. El nombre de archivo que me estaba causando problemas, ahora codificado correctamente se ve así:

Content-Disposition: attachment; nombre de archivo * = UTF-8''Museum% 20% E5% 8D% 9A% E7% 89% A9% E9 A6%% 86.jpg

public static String rfc5987_encode(final String s) throws UnsupportedEncodingException { 
    final byte[] s_bytes = s.getBytes("UTF-8"); 
    final int len = s_bytes.length; 
    final StringBuilder sb = new StringBuilder(len << 1); 
    final char[] digits = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 
    final byte[] attr_char = {'!','#','$','&','+','-','.','0','1','2','3','4','5','6','7','8','9',   'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','^','_','`',      'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','|', '~'}; 
    for (int i = 0; i < len; ++i) { 
     final byte b = s_bytes[i]; 
     if (Arrays.binarySearch(attr_char, b) >= 0) 
      sb.append((char) b); 
     else { 
      sb.append('%'); 
      sb.append(digits[0x0f & (b >>> 4)]); 
      sb.append(digits[b & 0x0f]); 
     } 
    } 

    return sb.toString(); 
} 
+0

Me gustaría señalar que el carácter ''' no está en la lista permitida de rfc5987. – Gedrox

+0

.. pero el carácter '\' '(símbolo de acento grave, retroceso) es. Han editado la respuesta. – Gedrox

Cuestiones relacionadas