getResourceAsStream () vs FileInputStream

173

Saya mencoba memuat file di webapp, dan saya mendapat FileNotFoundpengecualian ketika saya menggunakannya FileInputStream. Namun, menggunakan jalur yang sama, saya bisa memuat file ketika saya melakukannya getResourceAsStream(). Apa perbedaan antara kedua metode, dan mengapa satu bekerja sementara yang lain tidak?

Vivin Paliath
sumber

Jawaban:

256

The java.io.Filedan mendampingi bertindak pada sistem file disk lokal. Akar penyebab masalah Anda adalah bahwa jalur relatif dalam java.iobergantung pada direktori kerja saat ini. Yaitu direktori dari mana JVM (dalam kasus Anda: satu server web) dimulai. Misalnya ini mungkin C:\Tomcat\binatau sesuatu yang sama sekali berbeda, tetapi dengan demikian tidak C:\Tomcat\webapps\contextname atau apa pun yang Anda harapkan. Dalam proyek Eclipse yang normal, itu akan menjadi C:\Eclipse\workspace\projectname. Anda dapat mempelajari tentang direktori kerja saat ini dengan cara berikut:

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

Namun, direktori kerja sama sekali tidak dapat dikontrol secara pemrograman. Anda harus benar-benar lebih suka menggunakan jalur absolut di FileAPI daripada jalur relatif. Misalnya C:\full\path\to\file.ext.

Anda tidak ingin hardcode atau menebak jalur absolut dalam aplikasi Java (web). Itu hanya masalah portabilitas (yaitu berjalan di sistem X, tetapi tidak di sistem Y). Praktik yang normal adalah menempatkan sumber daya semacam itu di classpath , atau menambahkan path lengkapnya ke classpath (dalam IDE seperti Eclipse yang merupakan srcfolder dan "build path"). Dengan cara ini Anda bisa meraihnya dengan bantuan ClassLoaderoleh ClassLoader#getResource()atau ClassLoader#getResourceAsStream(). Itu dapat menemukan file relatif ke "root" dari classpath, seperti yang Anda temukan secara kebetulan. Dalam aplikasi web (atau aplikasi lain yang menggunakan banyak classloader), disarankan untuk menggunakan yang ClassLoaderdikembalikan oleh Thread.currentThread().getContextClassLoader()untuk ini sehingga Anda dapat melihat "di luar" konteks webapp juga.

Alternatif lain di webapps adalah ServletContext#getResource()dan rekannya ServletContext#getResourceAsStream(). Itu dapat mengakses file yang terletak di webfolder publik proyek webapp, termasuk /WEB-INFfolder. Ini ServletContexttersedia dalam servlets dengan getServletContext()metode yang diwarisi , Anda dapat menyebutnya apa adanya.

Lihat juga:

BalusC
sumber
5
@khylo: terkait: stackoverflow.com/questions/7952090/…
BalusC
27

getResourceAsStream adalah cara yang tepat untuk melakukannya untuk aplikasi web (seperti yang sudah Anda pelajari).

Alasannya adalah bahwa membaca dari sistem file tidak dapat berfungsi jika Anda mengemas aplikasi web Anda dalam PERANG. Ini adalah cara yang tepat untuk mengemas aplikasi web. Itu portabel seperti itu, karena Anda tidak bergantung pada jalur file absolut atau lokasi di mana server aplikasi Anda diinstal.

Duffymo
sumber
3
+1 - meskipun "tidak bisa bekerja" terlalu kuat. (Membaca dari sistem file dapat dibuat berfungsi, tetapi melakukannya dengan mudah adalah rumit ... dan lebih banyak kode, terutama jika sumber dayanya ada dalam JAR.)
Stephen C
1
duffy, jawaban yang sangat bagus dan Anda menjelaskan kesalahan saya, tetapi BalusC menjelaskan banyak hal - saya pikir jawabannya akan sangat membantu bagi orang-orang yang ingin mengetahui detail batiniah juga. Semoga Anda tidak keberatan saya mengubah jawaban yang diterima untuknya!
Vivin Paliath
@Stephen - Saya tidak berpikir "tidak bisa bekerja" terlalu kuat. Bahkan sesuatu yang sederhana seperti yang digunakan pada dua server berbeda dengan jalur yang berbeda ke server aplikasi akan mematahkannya. Intinya adalah bahwa Anda perlu membuat PERANG mandiri mungkin. Maksud Anda benar, tapi saya akan tetap berpegang pada kata-kata saya.
duffymo
14

FileInputStream akan memuat path file yang Anda lewati ke konstruktor sebagai relatif dari direktori kerja proses Java. Biasanya dalam wadah web, ini mirip binfolder.

getResourceAsStream()akan memuat path file relatif dari classpath aplikasi Anda .

matt b
sumber
12

The FileInputStreamkelas bekerja secara langsung dengan sistem file yang mendasari. Jika file yang dimaksud tidak ada secara fisik di sana, ia akan gagal membukanya. The getResourceAsStream()Metode bekerja secara berbeda. Ia mencoba untuk menemukan dan memuat sumber daya menggunakan ClassLoaderkelas yang dipanggilnya. Ini memungkinkannya menemukan, misalnya, sumber daya yang disematkan ke dalam jarfile.

Beladau
sumber
Nah, file dalam toples secara fisik masih "hadir" dalam sistem file, hanya ada di dalam file lain
matt b
1
Ya tentu saja. Tetapi mereka biasanya bukan sesuatu yang dilihat sebagai entitas independen dalam sistem file, kecuali jika aplikasi Anda mengetahui tentang jarformat file dan implikasinya. Dan di Jawa, yang tepat ClassLoadermungkin memiliki pengetahuan ini, sedangkan dataran FileInputStreamjelas tidak.
Dirk
7

classname.getResourceAsStream () memuat file melalui classloader dari classname. Jika kelas berasal dari file jar, dari situlah sumber daya akan diambil.

FileInputStream digunakan untuk membaca file dari sistem file.

Lachlan Roche
sumber
0

Saya di sini dengan memisahkan kedua penggunaan dengan menandainya sebagai File Read (java.io) dan Resource Read (ClassLoader.getResourceAsStream ()).

File Read - 1. Bekerja pada sistem file lokal. 2. Mencoba mencari file yang diminta dari direktori JVM yang diluncurkan saat ini sebagai root 3. Idealnya bagus saat menggunakan file untuk diproses di lokasi yang telah ditentukan seperti, / dev / file atau C: \ Data.

Baca Sumber Daya - 1. Bekerja pada jalur kelas 2. Mencoba untuk mencari file / sumber daya di classpather induk atau saat ini. 3. Idealnya bagus ketika mencoba memuat file dari file paket seperti war atau jar.

Aditya Bhuyan
sumber