getResourceAsStream () so với FileInputStream


173

Tôi đã cố tải một tập tin trong một ứng dụng web và tôi đã gặp một FileNotFoundngoại lệ khi tôi sử dụng FileInputStream. Tuy nhiên, bằng cách sử dụng cùng một đường dẫn, tôi đã có thể tải tệp khi tôi đã làm getResourceAsStream(). Sự khác biệt giữa hai phương pháp là gì và tại sao một phương thức hoạt động trong khi phương thức kia không?

Câu trả lời:


256

Các java.io.Filevà phối hợp hoạt động trên hệ thống tập tin đĩa cục bộ. Nguyên nhân gốc rễ của vấn đề của bạn là các đường dẫn tương đốijava.io phụ thuộc vào thư mục làm việc hiện tại. Tức là thư mục mà JVM (trong trường hợp của bạn: máy chủ webs) được khởi động. Điều này có thể là ví dụ C:\Tomcat\binhoặc một cái gì đó hoàn toàn khác, nhưng do đó không C:\Tomcat\webapps\contextname hoặc bất cứ điều gì bạn mong đợi nó sẽ xảy ra. Trong một dự án Eclipse bình thường, điều đó sẽ xảy ra C:\Eclipse\workspace\projectname. Bạn có thể tìm hiểu về thư mục làm việc hiện tại theo cách sau:

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

Tuy nhiên, thư mục làm việc không có cách nào kiểm soát được bằng lập trình. Bạn thực sự nên sử dụng các đường dẫn tuyệt đối trong FileAPI thay vì các đường dẫn tương đối. Ví dụ C:\full\path\to\file.ext.

Bạn không muốn mã hóa cứng hoặc đoán đường dẫn tuyệt đối trong các ứng dụng Java (web). Đó chỉ là sự cố về tính di động (nghĩa là nó chạy trong hệ thống X, nhưng không chạy trong hệ thống Y). Cách thực hành thông thường là đặt các loại tài nguyên đó trong đường dẫn lớp hoặc thêm đường dẫn đầy đủ của nó vào đường dẫn lớp (trong một IDE như Eclipse là srcthư mục và "đường dẫn xây dựng" tương ứng). Bằng cách này bạn có thể lấy chúng với sự giúp đỡ của ClassLoaderbằng ClassLoader#getResource() hoặc ClassLoader#getResourceAsStream(). Nó có thể xác định vị trí các tệp liên quan đến "gốc" của đường dẫn lớp, như bạn đã tìm ra. Trong ứng dụng web (hoặc bất kỳ ứng dụng nào khác sử dụng nhiều trình nạp lớp), chúng tôi khuyên bạn nên sử dụng ClassLoadernhư được trả về bởiThread.currentThread().getContextClassLoader() cho điều này để bạn cũng có thể nhìn "bên ngoài" bối cảnh ứng dụng web.

Một lựa chọn khác trong webapps là ServletContext#getResource()và đối tác của nó ServletContext#getResourceAsStream(). Nó có thể truy cập các tập tin nằm ở nơi công cộngweb thư mục chung của dự án webapp, bao gồm cả /WEB-INFthư mục. Có ServletContextsẵn trong các servlet bởi người thừa kếgetServletContext() phương thức , bạn có thể gọi nó là nguyên trạng.

Xem thêm:



27

getResourceAsStream là cách đúng đắn để làm điều đó cho các ứng dụng web (như bạn đã học).

Lý do là việc đọc từ hệ thống tệp không thể hoạt động nếu bạn đóng gói ứng dụng web của mình trong WAR. Đây là cách thích hợp để đóng gói một ứng dụng web. Nó di động theo cách đó, vì bạn không phụ thuộc vào đường dẫn tệp tuyệt đối hoặc vị trí nơi máy chủ ứng dụng của bạn được cài đặt.


3
+1 - mặc dù "không thể làm việc" quá mạnh. (Đọc từ hệ thống tập tin có thể được thực hiện để hoạt động, nhưng thực hiện nó một cách hợp lý là một việc khó khăn ... và nhiều mã hơn, đặc biệt là nếu tài nguyên nằm trong JAR.)
Stephen C

1
duffy, câu trả lời rất hay và bạn đã giải thích lỗi của tôi là gì, nhưng BalusC đã đi sâu vào chi tiết - tôi nghĩ câu trả lời của anh ấy sẽ hữu ích cho những người muốn biết chi tiết bên trong. Hy vọng bạn không phiền tôi thay đổi câu trả lời được chấp nhận cho anh ấy!
Vivin Paliath

@Stephen - Tôi không nghĩ "không thể làm việc" quá mạnh. Ngay cả một cái gì đó đơn giản như được triển khai trên hai máy chủ khác nhau với các đường dẫn khác nhau đến máy chủ ứng dụng sẽ phá vỡ nó. Vấn đề là bạn cần làm cho WAR của mình càng khép kín càng tốt. Quan điểm của bạn là chính xác, nhưng tôi sẽ tuân theo cách diễn đạt của tôi.
duffymo

14

FileInputStream sẽ tải một đường dẫn tệp mà bạn chuyển đến hàm tạo tương đối từ thư mục làm việc của quy trình Java. Thông thường trong một thùng chứa web, đây là một cái gì đó giống như binthư mục.

getResourceAsStream()sẽ tải một đường dẫn tệp tương đối từ đường dẫn ứng dụng của bạn .


12

Các FileInputStreamlớp học làm việc trực tiếp với các hệ thống tập tin cơ bản. Nếu tệp trong câu hỏi không có mặt vật lý ở đó, nó sẽ không mở được. Các getResourceAsStream()phương pháp làm việc khác nhau. Nó cố gắng định vị và tải tài nguyên bằng cách sử dụng ClassLoaderlớp mà nó được gọi. Điều này cho phép nó tìm, ví dụ, tài nguyên được nhúng vào jarcác tệp.


Chà, các tệp trong một tệp jar vẫn "hiện diện" về mặt vật lý trong một hệ thống tệp, chỉ chứa trong các tệp khác
matt b

1
Vâng, vâng, tất nhiên. Nhưng chúng thường không phải là một cái gì đó được coi là các thực thể độc lập trong hệ thống tệp, trừ khi ứng dụng của bạn tình cờ biết về jarđịnh dạng tệp và ý nghĩa của nó. Và trong Java, thích hợp ClassLoadercó thể có kiến ​​thức này, trong khi một đồng bằng FileInputStreamchắc chắn không có.
Dirk

7

classname.getResourceAsStream () tải một tệp thông qua trình nạp lớp của tên lớp. Nếu lớp đến từ một tệp jar, đó là nơi tài nguyên sẽ được tải từ đó.

FileInputStream được sử dụng để đọc một tệp từ hệ thống tệp.


0

Tôi ở đây bằng cách tách cả hai cách sử dụng bằng cách đánh dấu chúng là File Read (java.io) và Resource Read (ClassLoader.getResourceAsStream ()).

Đọc tệp - 1. Hoạt động trên hệ thống tệp cục bộ. 2. Thử định vị tệp được yêu cầu từ thư mục khởi chạy JVM hiện tại là root 3. Tốt nhất khi sử dụng các tệp để xử lý ở một vị trí được xác định trước như, / dev / files hoặc C: \ Data.

Tài nguyên đã đọc - 1. Hoạt động trên đường dẫn lớp 2. Cố gắng xác định vị trí tệp / tài nguyên trong đường dẫn lớp trình nạp lớp hiện tại hoặc lớp cha. 3. Tốt nhất khi tải các tệp từ các tệp được đóng gói như war hoặc jar.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.