2010-05-04 8 views
6

Tengo un servlet que necesita escribir archivos que tienen un nombre configurable por el usuario. Estoy tratando de usar la codificación URI para escapar correctamente de los caracteres especiales, pero el JRE parece convertir automáticamente barras diagonales codificadas %2F en separadores de ruta.¿Por qué Java decodifica automáticamente% 2F en nombres de archivos codificados URI?

Ejemplo:

File dir = new File("C:\Documents and Setting\username\temp"); 
String fn = "Top 1/2.pdf"; 
URI uri = new URI(dir.toURI().toASCIIString() + URLEncoder.encoder(fn, "ASCII").toString()); 
File out = new File(uri); 

System.out.println(dir.toURI().toASCIIString()); 
System.out.println(URLEncoder.encode(fn, "ASCII").toString()); 
System.out.println(uri.toASCIIString()); 
System.out.println(output.toURI().toASCIIString()); 

La salida es:

file:/C:/Documents%20and%20Settings/username/temp/ 
Top+1%2F2.pdf 
file:/C:/Documents%20and%20Settings/username/temp/Top+1%2F2.pdf 
file:/C:/Documents%20and%20Settings/username/temp/Top+1/2.pdf 

Después se crea una instancia del objeto nuevo archivo, la secuencia de %2F se convierte automáticamente en una barra diagonal y termina con una ruta incorrecta . ¿Alguien sabe la forma correcta de abordar este problema?

El núcleo del problema parece ser que

uri.equals(new File(uri).toURI()) == FALSE 

cuando hay una %2F en el URI.

Planeo usar la cadena URLEncoded textualmente en lugar de intentar usar el constructor File(uri).

+0

Es ciertamente correcto para hacerlo. Si necesita una literal "% 2F" en el nombre del archivo, debe escaparse correctamente cuando se utiliza en un URI: '% 252F' – Joey

+0

Creo que no entiende bien. No quiero codificar '% 2F', quiero codificar '/' para poder crear un nombre de archivo legal que represente un nombre especificado por el usuario que contenga la barra diagonal. – Lucas

+0

Fuera de interés: Sé que estos son URI de archivos, pero si fueran URI http ¿no deberían los servidores web manejar '% F2' y'/'lo mismo? Además: dado que '/' no es válido en el nombre de archivo de una ventana, dicho URI de archivo no parece tener sentido, o? – RoToRa

Respuesta

5

El new File(URI) construye el archivo según la ruta obtenida por URI#getPath() en lugar de -lo que esperaba- URI#getRawPath(). Esta parece una característica "por diseño".

tiene 2 opciones:

  1. Run URLEncoder#encode() en fn dos veces (nota: encode(), no encoder()).
  2. Use new File(String) en su lugar.
+0

Tiene sentido. Esto se documentó en cualquier lugar, o está implícito en la garantía de clase de archivo que "nuevo archivo (f.toURI()). Es igual a (f.getAbsoluteFile())"? – Lucas

+0

@Lucas: no está documentado; ver mi respuesta –

+0

Eso, y también el código fuente :) – BalusC

2

Creo que @BalusC ha solucionado el problema directo en su código. Solo me gustaría señalar algún otro issuse

Las expresiones dir.toURI().toASCIIString() y URLEncoder.encoder(fn, "UTF-8").toString() realmente hacen cosas bastante diferentes.

  • El primero, codifica el URI como una cadena, la aplicación de las reglas de codificación de URI de acuerdo con la gramática URI. Entonces, por ejemplo, un '/' en el componente de ruta no será codificado, pero un '/' en la consulta o los componentes del fragmento se codificarán como% 2F.

  • El segundo código codifica la cadena fn aplicando las reglas de codificación sin referencia al contenido de la cadena. mapeo

El File(URI) del constructor de un archivo URI a un archivo es system dependent and undocumented. Estoy un poco sorprendido de que decodifique el %2F, pero hace lo que hace, y @BalusC explica por qué. La conclusión es que es potencialmente problemático utilizar un mecanismo ("archivo:" URI) que dependen explícitamente del sistema.

Por último, es incorrecto combinar esas cadenas de componentes de URI como esas.Debe ser

URI uri = new URI(
     dir.toURI().toString() + 
     URLEncoder.encoder(fn, "UTF-8").toString(); 

o

URI uri = new URI(
     dir.toURI().toASCIIString() + 
     URLEncoder.encoder(fn, "ASCII").toString()); 
Cuestiones relacionadas