getResourceAsStream () vs FileInputStream

173

Intenté cargar un archivo en una aplicación web y recibí una FileNotFoundexcepción cuando lo usé FileInputStream. Sin embargo, usando la misma ruta, pude cargar el archivo cuando lo hice getResourceAsStream(). ¿Cuál es la diferencia entre los dos métodos y por qué uno funciona mientras que el otro no?

Vivin Paliath
fuente

Respuestas:

256

El java.io.Filey consorts actúa en el sistema de archivos del disco local. La causa raíz de su problema es que las rutas relativasjava.io dependen del directorio de trabajo actual. Es decir, el directorio desde el que se inicia la JVM (en su caso: la del servidor web). Esto puede ser, por ejemplo, C:\Tomcat\bino algo completamente diferente, pero por lo tanto noC:\Tomcat\webapps\contextname o lo que sea que espere que sea. En un proyecto Eclipse normal, eso sería C:\Eclipse\workspace\projectname. Puede obtener información sobre el directorio de trabajo actual de la siguiente manera:

System.out.println(new File(".").getAbsolutePath());

Sin embargo, el directorio de trabajo no es controlable programáticamente de ninguna manera. Realmente debería preferir usar rutas absolutas en la FileAPI en lugar de rutas relativas. Por ej C:\full\path\to\file.ext.

No desea codificar ni adivinar la ruta absoluta en las aplicaciones Java (web). Eso es solo un problema de portabilidad (es decir, se ejecuta en el sistema X, pero no en el sistema Y). La práctica normal es colocar ese tipo de recursos en el classpath , o agregar su ruta completa al classpath (en un IDE como Eclipse esa es la srccarpeta y la "ruta de compilación" respectivamente). De esta manera se puede agarrar con la ayuda de ClassLoaderpor ClassLoader#getResource()o ClassLoader#getResourceAsStream(). Es capaz de localizar archivos relativos a la "raíz" de la ruta de clase, como usted descubrió por coincidencia. En aplicaciones web (o cualquier otra aplicación que use múltiples cargadores de clases), se recomienda usar el ClassLoaderque se devuelve Thread.currentThread().getContextClassLoader()para esto para que también pueda mirar "fuera" del contexto de la aplicación web.

Otra alternativa en webapps es la ServletContext#getResource()y su contraparte ServletContext#getResourceAsStream(). Puede acceder a los archivos ubicados en la webcarpeta pública del proyecto de aplicación web, incluida la /WEB-INFcarpeta. El ServletContextestá disponible en servlets por el getServletContext()método heredado , puede llamarlo como está.

Ver también:

BalusC
fuente
55
@khylo: relacionado: stackoverflow.com/questions/7952090/…
BalusC
27

getResourceAsStream es la forma correcta de hacerlo para aplicaciones web (como ya aprendiste).

La razón es que la lectura del sistema de archivos no puede funcionar si empaqueta su aplicación web en un WAR. Esta es la forma correcta de empaquetar una aplicación web. Es portátil de esa manera, porque no depende de una ruta de archivo absoluta o de la ubicación donde está instalado su servidor de aplicaciones.

duffymo
fuente
3
+1 - aunque "no puede funcionar" es demasiado fuerte. (Se puede hacer que la lectura del sistema de archivos funcione, pero hacerlo de forma portátil es complicado ... y mucho más código, especialmente si el recurso está en un JAR.)
Stephen C
1
duffy, muy buena respuesta y me explicaste cuál fue mi error, pero BalusC entró en muchos detalles: creo que su respuesta sería útil para las personas que también quisieran conocer los detalles internos. ¡Espero que no te importe que cambie la respuesta aceptada a la suya!
Vivin Paliath
@Stephen - No creo que "no pueda funcionar" sea demasiado fuerte. Incluso algo tan simple como implementarse en dos servidores diferentes con diferentes rutas al servidor de aplicaciones lo romperá. El punto es que debes hacer que tu WAR sea lo más autónoma posible. Su punto es correcto, pero voy a seguir con mi redacción.
duffymo
14

FileInputStream cargará una ruta de archivo que pase al constructor como relativa desde el directorio de trabajo del proceso Java. Por lo general, en un contenedor web, esto es algo así como la bincarpeta.

getResourceAsStream()cargará una ruta de archivo relativa desde la ruta de clase de su aplicación .

mate b
fuente
12

La FileInputStreamclase funciona directamente con el sistema de archivos subyacente. Si el archivo en cuestión no está físicamente presente allí, no podrá abrirlo. El getResourceAsStream()método funciona de manera diferente. Intenta localizar y cargar el recurso utilizando el ClassLoaderde la clase a la que se llama. Esto le permite encontrar, por ejemplo, recursos incrustados en jararchivos.

Puñal
fuente
Bueno, los archivos dentro de un jar todavía están físicamente "presentes" en un sistema de archivos, solo contenidos en otros archivos
mate b
1
Pues sí, por supuesto. Pero generalmente no son algo visto como entidades independientes en el sistema de archivos, a menos que su aplicación conozca el jarformato del archivo y sus implicaciones. Y en Java, lo apropiado ClassLoaderpodría tener este conocimiento, mientras que un plano FileInputStreamciertamente no lo tiene.
Dirk
7

classname.getResourceAsStream () carga un archivo a través del cargador de clases de classname. Si la clase provino de un archivo jar, desde allí se cargará el recurso.

FileInputStream se usa para leer un archivo del sistema de archivos.

Lachlan Roche
fuente
0

Estoy aquí separando ambos usos marcándolos como Lectura de archivo (java.io) y Lectura de recursos (ClassLoader.getResourceAsStream ()).

Lectura de archivos - 1. Funciona en el sistema de archivos local. 2. Intenta ubicar el archivo solicitado desde el directorio actual lanzado por JVM como root 3. Idealmente bueno cuando se usan archivos para procesar en una ubicación predeterminada como / dev / files o C: \ Data.

Lectura de recursos: 1. Funciona en la ruta de clase 2. Intenta ubicar el archivo / recurso en la ruta de clase del cargador de clases actual o principal. 3. Idealmente bueno cuando se intenta cargar archivos de archivos empaquetados como war o jar.

Aditya Bhuyan
fuente