getResourceAsStream () vs FileInputStream


173

Stavo cercando di caricare un file in una webapp e stavo ottenendo FileNotFoundun'eccezione quando l'ho usato FileInputStream. Tuttavia, utilizzando lo stesso percorso, sono stato in grado di caricare il file quando l'ho fatto getResourceAsStream(). Qual è la differenza tra i due metodi e perché uno funziona mentre l'altro no?

Risposte:


256

Il java.io.Filee consorts agisce sul file system del disco locale. La causa principale del problema è che i percorsi relativi in java.iodipendono dalla directory di lavoro corrente. Vale a dire la directory da cui viene avviata la JVM (nel tuo caso: quella del server web). Questo può essere, ad esempio, C:\Tomcat\bino qualcosa di completamente diverso, ma quindi no C:\Tomcat\webapps\contextname o qualunque cosa ti aspetti. In un normale progetto Eclipse, sarebbe C:\Eclipse\workspace\projectname. Puoi conoscere l'attuale directory di lavoro nel modo seguente:

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

Tuttavia, la directory di lavoro non è in alcun modo controllabile a livello di programmazione. È preferibile utilizzare percorsi assolutiFile nell'API anziché percorsi relativi. Per esempioC:\full\path\to\file.ext .

Non si desidera hardcode o indovinare il percorso assoluto nelle applicazioni Java (web). Questo è solo un problema di portabilità (cioè funziona nel sistema X, ma non nel sistema Y). La pratica normale è quella di mettere quel tipo di risorse nel percorso di classe , o di aggiungere il suo percorso completo al percorso di classe (in un IDE come Eclipse che è rispettivamente la srccartella e il "percorso di costruzione"). In questo modo li si può afferrare con l'aiuto del ClassLoaderby ClassLoader#getResource()o ClassLoader#getResourceAsStream(). È in grado di individuare i file relativi alla "radice" del percorso di classe, come hai capito per caso. Nelle applicazioni web (o in qualsiasi altra applicazione che utilizza più classloader) si consiglia di utilizzare il valore ClassLoaderrestituito Thread.currentThread().getContextClassLoader()per questo in modo da poter apparire "al di fuori" del contesto webapp.

Un'altra alternativa nelle webapp è la ServletContext#getResource()e la sua controparte ServletContext#getResourceAsStream(). È in grado di accedere ai file situati nella webcartella pubblica del progetto webapp, inclusa la /WEB-INFcartella. Il ServletContextè disponibile in servlet dall'ereditatogetServletContext() metodo, si può chiamare così com'è.

Guarda anche:



27

getResourceAsStream è il modo giusto di farlo per le app Web (come hai già appreso).

Il motivo è che la lettura dal file system non può funzionare se si confeziona l'app Web in un WAR. Questo è il modo corretto di impacchettare un'app Web. È portatile in questo modo, perché non dipendi da un percorso di file assoluto o dalla posizione in cui è installato l'app server.


3
+1 - anche se "non può funzionare" è troppo forte. (La lettura dal filesystem può essere fatta funzionare, ma farlo in modo portabile è un trucco ... e molto più codice, specialmente se la risorsa è in un JAR.)
Stephen C

1
Duffy, molto bella risposta e mi hai spiegato qual è stato il mio errore, ma BalusC è entrato in molti dettagli - penso che la sua risposta sarebbe stata utile per le persone che vorrebbero conoscere anche i dettagli interiori. Spero non ti dispiaccia che io cambi la risposta accettata alla sua!
Vivin Paliath,

@Stephen - Non credo che "impossibile funzionare" sia troppo forte. Anche qualcosa di semplice come essere distribuito su due server diversi con percorsi diversi al server delle app lo interromperà. Il punto è che devi rendere la tua GUERRA il più autonoma possibile. Il tuo punto è corretto, ma mi atterrò alle mie parole.
duffymo,

14

FileInputStream caricherà il percorso del file che si passa al costruttore come relativo dalla directory di lavoro del processo Java. Di solito in un contenitore Web, questo è qualcosa comebin cartella.

getResourceAsStream()caricherà un percorso di file relativo dal percorso di classe dell'applicazione .


12

Il FileInputStream classe funziona direttamente con il file system sottostante. Se il file in questione non è fisicamente presente lì, non riuscirà ad aprirlo. Il getResourceAsStream()metodo funziona in modo diverso. Cerca di localizzare e caricare la risorsa usando ClassLoaderla classe su cui è chiamata. Ciò gli consente di trovare, ad esempio, risorse incorporate nei jarfile.


Bene, i file all'interno di un barattolo sono ancora fisicamente "presenti" in un file system, contenuti solo in altri file
matt b

1
Beh, certo, certo. Ma di solito non sono considerati entità indipendenti nel file system, a meno che l'applicazione non conosca il jarformato del file e le sue implicazioni. E in Java, l'appropriato ClassLoaderpotrebbe avere questa conoscenza, mentre una pianura FileInputStreamcertamente no.
Dirk,

7

classname.getResourceAsStream () carica un file tramite il classloader di classname. Se la classe proviene da un file jar, è da lì che verrà caricata la risorsa.

FileInputStream viene utilizzato per leggere un file dal filesystem.


0

Sono qui separando entrambi gli usi contrassegnandoli come File Read (java.io) e Resource Read (ClassLoader.getResourceAsStream ()).

File letto - 1. Funziona sul file system locale. 2. Cerca di individuare il file richiesto dall'attuale directory avviata da JVM come root 3. Idealmente buono quando si utilizzano i file per l'elaborazione in una posizione predeterminata come, / dev / files o C: \ Data.

Lettura delle risorse - 1. Funziona sul percorso di classe 2. Cerca di individuare il file / risorsa nel percorso di classe del classloader corrente o principale. 3. Idealmente buono quando si tenta di caricare file da file compressi come war o jar.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.