Sự khác biệt giữa Tài nguyên, URI, URL, Đường dẫn và Tệp trong Java là gì?


95

Tôi đang xem một đoạn mã Java ngay bây giờ, và nó lấy một đường dẫn dưới dạng Chuỗi và sử dụng URL của nó URL resource = ClassLoader.getSystemClassLoader().getResource(pathAsString);, sau đó gọi String path = resource.getPath()và cuối cùng thực thi new File(path);.

Ồ, và cũng có những cuộc gọi đến URL url = resource.toURI();String file = resource.getFile().

Tôi hoàn toàn bối rối ngay bây giờ - chủ yếu là vì thuật ngữ, tôi đoán vậy. Ai đó có thể vui lòng hướng dẫn tôi về những điểm khác biệt hoặc cung cấp một vài liên kết đến tài liệu Chống giả không? Đặc biệt là URI tới URL và Resource to File ? Đối với tôi, có vẻ như chúng phải giống nhau ...

Sự khác biệt giữa getFile()getPath()được giải thích ở đây: Sự khác biệt giữa url.getFile () và getpath () là gì? (Điều thú vị là cả hai dường như trả về Chuỗi, điều này có thể bổ sung rất nhiều cho trạng thái tâm trí của tôi ...)

Bây giờ, nếu tôi có một bộ định vị tham chiếu đến một lớp hoặc gói trong một tệp jar, thì hai cái đó (tức là đường dẫn một chuỗi tệp) có khác nhau không?

resource.toString()sẽ cung cấp cho bạn jar:file:/C:/path/to/my.jar!/com/example/, sau tất cả (lưu ý dấu chấm than).

Có phải sự khác biệt giữa URIURL trong Java mà trước đây không mã hóa khoảng trắng? Cf Tệp, URI và URL xung đột trong Java (Câu trả lời này giải thích khá rõ sự khác biệt chung về khái niệm giữa hai thuật ngữ: URI xác định và URL định vị; )

Cuối cùng - và quan trọng nhất - tại sao tôi cần Fileđối tượng; tại sao một Resource ( URL) không đủ? (Và có đối tượng Tài nguyên không?)

Xin lỗi nếu câu hỏi này hơi thiếu tổ chức; nó chỉ phản ánh sự nhầm lẫn mà tôi có ... :)


5
Và bạn thậm chí còn không bắt đầu xem Pathvà FileSystem từ NIO :)
eckes

2
@eckes Mỗi lần đau đầu, làm ơn. ;)
Christian

1
Trong bối cảnh câu hỏi của bạn, Tệp / URL + URI không liên quan. Phương thức này là phương tiện để đặt tên và thao tác trên các tệp tin còn lại là phương thức đặt tên và đọc từ các tài nguyên (có thể là các tệp tin). Phương thức getFile và getPath xử lý các thành phần của URL được đặt tên (gây nhầm lẫn) giống như các đối tượng tệp. Tài nguyên của trình tải lớp không được biểu thị dưới dạng tệp vì chúng có thể có nguồn gốc khác nhau (hoặc được lồng trong tệp JAR).
eckes

1
Tôi xin lưu ý rằng mã này không có khả năng hoạt động như dự định. A URLkhông rõ ràng - như bạn hiển thị jar:file:, tức là một tài nguyên trong .jarkho lưu trữ. Ghép cái đó vào a Filerất khó có thể mang lại bất cứ điều gì hữu ích.
Boris the Spider,

1
Trọng tâm của vấn đề của bạn là các từ resourcepath có thể có các nghĩa khác nhau, tùy thuộc vào ngữ cảnh.
Raedwald

Câu trả lời:


43

CẬP NHẬT 2017-04-12 Kiểm tra câu trả lời của JvR vì nó chứa lời giải thích đầy đủ và chính xác hơn!


Xin lưu ý rằng tôi không tự cho mình là đủ 100% khả năng trả lời, nhưng tuy nhiên, đây là một số nhận xét:

  • File đại diện cho một tệp hoặc thư mục có thể truy cập thông qua hệ thống tệp
  • resource là một thuật ngữ chung cho một đối tượng dữ liệu có thể được tải bởi ứng dụng
    • thông thường tài nguyên là các tệp được phân phối cùng với ứng dụng / thư viện và được tải qua cơ chế tải lớp (khi chúng nằm trên đường dẫn lớp)
  • URL#getPathlà getter trên phần đường dẫn của URL ( protocol://host/path?query)
  • URL#getFile theo JavaDoc trả về path+query

Trong Java, URIchỉ là một cấu trúc dữ liệu để thao tác với chính mã định danh chung.

URLmặt khác thực sự là một bộ định vị tài nguyên và cung cấp cho bạn các tính năng để thực sự đọc tài nguyên thông qua URLStreamHandlercác s đã đăng ký .

URL có thể dẫn đến tài nguyên hệ thống tệp và bạn có thể tạo URL cho mọi tài nguyên hệ thống tệp bằng cách sử dụng file://giao thức (do đó quan hệ File<-> URL).

Cũng cần biết rằng điều đó URL#getFilekhông liên quan đến java.io.File.


Tại sao tôi cần đối tượng Tệp; tại sao một Tài nguyên (URL) không đủ?

Đủ rồi. Chỉ khi bạn muốn chuyển tài nguyên cho một số thành phần chỉ có thể hoạt động với các tệp, bạn cần phải lấy Filetừ nó. Tuy nhiên, không phải tất cả các URL tài nguyên đều có thể được chuyển đổi thành Files.

Và có đối tượng Tài nguyên không?

Theo quan điểm của JRE, nó chỉ là một thuật ngữ. Một số khung công tác cung cấp cho bạn lớp như vậy (ví dụ: Spring's Resource ).


5
Ngoài ra còn có java.nio.file.Path, về cơ bản là một sự thay thế (Java 7+) java.io.File, vì API thứ hai dường như không được nghĩ ra trong những ngày đầu của Java.
ntoskrnl

1
Nói chung, bạn nên giảm thiểu việc sử dụng URL trừ khi nó thực sự cần thiết. Lý do là các phương thức equalshashCode của URL được triển khai theo một cách đáng ngạc nhiên: chúng đang chặn các cuộc gọi phương thức.
kibibyte

3
@kibibyte: Tôi hy vọng cuộc gọi sẽ bị chặn, triển khai mã băm và bằng không đồng bộ bây giờ sẽ rất đáng lo ngại. Tôi nghĩ ý của bạn là các cuộc gọi sẽ thử và giải quyết máy chủ để tìm xem chúng có tương đương hay không và do đó có thể thực hiện chặn các cuộc gọi mạng.
Newtopian

50

Tôi hoàn toàn bối rối ngay bây giờ - chủ yếu là vì thuật ngữ, tôi đoán vậy. Ai đó có thể vui lòng hướng dẫn tôi về những điểm khác biệt hoặc cung cấp một vài liên kết đến tài liệu Chống giả không? Đặc biệt là URI tới URL và Resource to File? Đối với tôi, có vẻ như chúng phải giống nhau ...

Thuật ngữ này gây nhầm lẫn và đôi khi gây bối rối, và hầu hết được sinh ra từ sự phát triển của cả Java như một API và một nền tảng theo thời gian. Để hiểu các thuật ngữ này có ý nghĩa như thế nào, điều quan trọng là phải nhận ra hai điều ảnh hưởng đến thiết kế của Java:

  • Khả năng tương thích ngược. Các ứng dụng cũ nên chạy trên các bản cài đặt mới hơn, lý tưởng nhất là không cần sửa đổi. Điều này có nghĩa là một API cũ (với tên và thuật ngữ của nó) cần được duy trì thông qua tất cả các phiên bản mới hơn.
  • Đa nền tảng. API phải cung cấp phần tóm tắt có thể sử dụng được của nền tảng cơ bản của nó, cho dù đó là hệ điều hành hay trình duyệt.

Tôi sẽ đi qua các khái niệm và cách chúng hình thành. Tôi sẽ trả lời các câu hỏi cụ thể khác của bạn sau đó, vì tôi có thể phải đề cập đến điều gì đó trong phần đầu tiên.

"Tài nguyên" là gì?

Một phần dữ liệu trừu tượng, chung chung có thể được định vị và đọc. Nói một cách dễ hiểu, Java sử dụng điều này để chỉ một "tệp" có thể không phải là tệp nhưng đại diện cho một phần dữ liệu được đặt tên. Nó không có lớp trực tiếp hoặc đại diện giao diện trong Java , nhưng do các thuộc tính của nó (có thể định vị, có thể đọc được) nên nó thường được biểu diễn bằng một URL.

Bởi vì một trong những mục tiêu thiết kế ban đầu của Java là được chạy bên trong trình duyệt, như một ứng dụng hộp cát (applet!) Với các quyền / đặc quyền / giải phóng mặt bằng bảo mật rất hạn chế, Java tạo ra sự khác biệt rõ ràng (lý thuyết) giữa một tệp (một cái gì đó trên cục bộ hệ thống tệp) và một tài nguyên (thứ mà nó cần để đọc). Đây là lý do tại sao việc đọc một cái gì đó liên quan đến ứng dụng (biểu tượng, tệp lớp, v.v.) được thực hiện thông qua ClassLoader.getResourcechứ không phải thông qua lớp tệp.

Thật không may, vì "tài nguyên" cũng là một thuật ngữ chung hữu ích bên ngoài cách giải thích này, nó cũng được sử dụng để đặt tên cho những thứ rất cụ thể (ví dụ: Nhóm ResourceBundle , UIResource , Tài nguyên ), theo nghĩa này, không phải là một tài nguyên.

Các lớp chính đại diện (một đường dẫn đến) một tài nguyên là java.nio.file.Path , java.io.File , java.net.URIjava.net.URL .

Tệp (java.io, 1.0)

Một đại diện trừu tượng của tên đường dẫn tệp và thư mục.

Lớp Tệp đại diện cho một tài nguyên có thể truy cập được thông qua hệ thống tệp gốc của nền tảng . Nó chỉ chứa tên của tệp, vì vậy nó thực sự là một đường dẫn (xem phần sau) mà nền tảng máy chủ lưu trữ diễn giải theo các cài đặt, quy tắc và cú pháp của riêng nó.

Lưu ý rằng Tệp không cần phải trỏ đến một cái gì đó cục bộ , chỉ một cái gì đó mà nền tảng máy chủ lưu trữ hiểu được trong ngữ cảnh truy cập tệp, ví dụ: đường dẫn UNC trong Windows. Nếu bạn gắn tệp ZIP làm hệ thống tệp trong hệ điều hành của mình, thì Tệp sẽ đọc tốt các mục nhập chứa nó.

URL (java.net, 1.0)

URL lớp đại diện cho Bộ định vị tài nguyên thống nhất, một con trỏ đến "tài nguyên" trên World Wide Web. Tài nguyên có thể là một cái gì đó đơn giản như một tệp hoặc một thư mục, hoặc nó có thể là một tham chiếu đến một đối tượng phức tạp hơn, chẳng hạn như truy vấn đến cơ sở dữ liệu hoặc công cụ tìm kiếm.

Song song với khái niệm tài nguyên, URL biểu thị tài nguyên đó giống như cách lớp Tệp đại diện cho tệp trong nền tảng máy chủ: như một chuỗi có cấu trúc trỏ đến một tài nguyên. URL cũng chứa một lược đồ gợi ý về cách tiếp cận tài nguyên (với "tệp:" là "yêu cầu nền tảng máy chủ lưu trữ") và do đó, cho phép trỏ vào tài nguyên thông qua HTTP, FTP, bên trong JAR, và không.

Thật không may, URL đi kèm với cú pháp và thuật ngữ riêng, bao gồm cả việc sử dụng "tệp" và "đường dẫn". Trong trường hợp URL là tệp-URL, URL.getFile sẽ trả về một chuỗi giống với chuỗi đường dẫn của tệp được tham chiếu.

Class.getResource trả về một URL: nó linh hoạt hơn so với trả về Tệp và nó đã phục vụ nhu cầu của hệ thống như tưởng tượng vào đầu những năm 1990.

URI (java.net, 1.4)

Đại diện cho tham chiếu Mã định danh tài nguyên đồng nhất (URI).

URI là một sự trừu tượng (nhẹ) so với URL. Sự khác biệt giữa URI và URL là khái niệm và chủ yếu là học thuật, nhưng URI được định nghĩa tốt hơn theo nghĩa chính thức và bao gồm nhiều trường hợp sử dụng hơn. Vì URL và URI không giống nhau, một lớp mới đã được giới thiệu để đại diện cho chúng, với các phương thức URI.toURL và URL.toURI để di chuyển giữa cái này và cái kia.

Trong Java, sự khác biệt chính giữa URL và URI là một URL mang kỳ vọng là có thể phân giải được , ứng dụng có thể muốn một InputStream từ đó; một URI được coi giống như một thứ trừu tượng có thể trỏ đến một thứ gì đó có thể giải quyết được (và thường là vậy), nhưng ý nghĩa của nó và cách tiếp cận nó thì dễ hiểu hơn về ngữ cảnh và cách diễn giải.

Đường dẫn (java.nio.file, 1.7)

Một đối tượng có thể được sử dụng để định vị tệp trong hệ thống tệp. Nó thường đại diện cho một đường dẫn tệp phụ thuộc vào hệ thống.

API tệp mới, được biểu tượng hóa trong giao diện Đường dẫn, cho phép tính linh hoạt cao hơn nhiều so với lớp Tệp có thể cung cấp. Giao diện Đường dẫn là một phần trừu tượng của lớp Tệp và là một phần của API Tệp ​​IO Mới . Trong đó Tệp nhất thiết phải trỏ đến một "tệp" như được hiểu bởi nền tảng máy chủ lưu trữ, Đường dẫn mang tính chung chung hơn: nó đại diện cho một tệp (tài nguyên) trong một hệ thống tệp tùy ý .

Đường dẫn làm mất đi sự phụ thuộc vào khái niệm tệp của nền tảng máy chủ. Nó có thể là một mục nhập trong tệp ZIP, một tệp có thể truy cập thông qua FTP hoặc SSH-FS, một biểu diễn đa gốc của classpath ứng dụng hoặc thực sự là bất kỳ thứ gì có thể được biểu diễn một cách có ý nghĩa thông qua giao diện FileSystem và trình điều khiển của nó, FileSystemProvider. Nó mang lại sức mạnh của việc "gắn kết" hệ thống tệp vào ngữ cảnh của một ứng dụng Java.

Nền tảng máy chủ lưu trữ được đại diện thông qua "hệ thống tệp mặc định"; khi bạn gọi File.toPath, bạn nhận được một Đường dẫn trên hệ thống tệp mặc định.


Bây giờ, nếu tôi có một bộ định vị tham chiếu đến một lớp hoặc gói trong một tệp jar, thì hai cái đó (tức là đường dẫn một chuỗi tệp) có khác nhau không?

Không có khả năng. Nếu tệp jar nằm trên hệ thống tệp cục bộ, bạn không nên có thành phần truy vấn, vì vậy URL.getPathURL.getFilesẽ trả về cùng một kết quả. Tuy nhiên, hãy chọn cái bạn cần: URL tệp thường có thể không có các thành phần truy vấn, nhưng tôi chắc chắn vẫn có thể thêm một thành phần.

Cuối cùng - và quan trọng nhất - tại sao tôi cần đối tượng Tệp; tại sao một Tài nguyên (URL) không đủ?

URL có thể không đủ vì Tệp cung cấp cho bạn quyền truy cập vào dữ liệu quản lý, chẳng hạn như quyền (có thể đọc, có thể ghi, có thể thực thi), loại tệp (tôi có phải là thư mục không?) Và khả năng tìm kiếm và thao tác với hệ thống tệp cục bộ. Nếu đây là những tính năng bạn cần, thì Tệp hoặc Đường dẫn sẽ cung cấp chúng.

Bạn không cần Tệp nếu bạn có quyền truy cập vào Đường dẫn. Tuy nhiên, một số API cũ hơn có thể yêu cầu Tệp.

(Và có đối tượng Tài nguyên không?)

Không, không có. Có rất nhiều thứ được đặt tên như vậy, nhưng chúng không phải là một nguồn lực theo nghĩa ClassLoader.getResource.


Wow, rất kỹ lưỡng. Chỉ mới xem qua, nhưng đã có câu hỏi tiếp theo đầu tiên: Khi bạn nói Tệp "chỉ chứa tên của tệp", bạn không mâu thuẫn với tuyên bố ban đầu của mình rằng đó là "Bản trình bày trừu tượng của tên đường dẫn tệp và thư mục" - còn nhiều không?
Christian

1
@Christian Ý tôi là "chỉ tên" như trong: không mô hình hóa nội dung của tệp theo bất kỳ cách nào; nó chỉ đơn thuần là một lớp bọc mỏng xung quanh một chuỗi. Phần "biểu diễn trừu tượng" được trích dẫn từ tài liệu API. ;)
JvR

Câu trả lời này xứng đáng nhận được nhiều sự ủng hộ hơn nữa ... sẽ cập nhật câu trả lời được chấp nhận của tôi để chỉ cho người đọc câu này.
Pavel Horal

12

Câu trả lời của Pavel Horal rất hay.

Như anh ấy nói, từ "file" có nghĩa hoàn toàn khác (thực tế không liên quan) trong URL#getFilevs java.io.File- có thể đó là một phần của sự nhầm lẫn.

Chỉ cần thêm:

  • Một tài nguyên trong Java là một khái niệm trừu tượng, một nguồn dữ liệu có thể được đọc. Vị trí (hoặc địa chỉ) của tài nguyên được biểu diễn trong Java bởi một URLđối tượng.

  • Một tài nguyên có thể tương ứng với một tệp thông thường trong hệ thống tệp cục bộ (cụ thể là khi nó URLbắt đầu bằng file://). Nhưng một tài nguyên thì tổng quát hơn (nó cũng có thể là một số tệp được lưu trữ trong một jar, hoặc một số dữ liệu được đọc từ mạng, hoặc từ bộ nhớ, hoặc ...). Và nó cũng hạn chế hơn, bởi vì a File(ngoài việc là những thứ khác với tệp thông thường: thư mục, liên kết) cũng có thể được tạo và ghi vào.

  • Hãy nhớ trong Java một Fileđối tượng không thực sự đại diện cho "một tệp" mà là vị trí (tên đầy đủ, kèm theo đường dẫn) của một tệp. Vì vậy, một Fileđối tượng cho phép bạn định vị (và mở) một tệp, như một đối tượng cho phép bạn URLtruy cập (và mở) một tài nguyên. (Không có Resourcelớp nào trong Java để đại diện cho một tài nguyên, nhưng cũng không có lớp nào để đại diện cho một tệp! Một lần nữa: Filekhông phải là một tệp, đó là đường dẫn của một tệp).


3

Theo tôi hiểu, bạn có thể phân loại chúng như sau:

Dựa trên web: URI và URL.

  • URL: URL là một vị trí xác định trên internt (chỉ là một địa chỉ web bình thường như - stackoverflow.com)
  • URI: Đã bao giờ URL là một URI. Nhưng các URI cũng có thể chứa những thứ như "mailto:", vì vậy chúng cũng có một số điều của "script" mà tôi muốn nói.

Và cục bộ: Tài nguyên, Đường dẫn và Tệp

  • Tài nguyên: Tài nguyên là các tệp bên trong jar của bạn. Chúng được sử dụng để tải tệp ra khỏi lọ / hộp đựng.
  • Đường dẫn: Đường dẫn về cơ bản là một chuỗi. Nhưng nó đi kèm với một số chức năng tiện dụng để nối nhiều chuỗi hoặc thêm tệp vào một chuỗi. Nó đảm bảo rằng con đường bạn đang xây dựng là hợp lệ.
  • Tệp: Đây là một tham chiếu đến một thư mục hoặc tệp. Nó được sử dụng để sửa đổi các tệp, mở chúng, v.v.

Sẽ dễ dàng hơn nếu chúng được hợp nhất thành một lớp - chúng thật sự rất khó hiểu: D

Tôi hy vọng cái này sẽ giúp bạn :)

(Tôi vừa xem qua tài liệu - xem docs.oracle.com)


0

Tệp là một đại diện trừu tượng của một thực thể trong hệ thống tệp cục bộ.

Đường dẫn thường là một chuỗi chỉ ra vị trí của tệp trong hệ thống tệp. Nó thường không bao gồm tên tệp. Vì vậy, c: \ Documents \ mystuff \uff.txt sẽ có một đường dẫn với giá trị là "C: \ Documents \ mystuff" Rõ ràng là định dạng của tên tệp tuyệt đối và đường dẫn sẽ rất khác nhau giữa các hệ thống tệp.

URL là một tập hợp URI với URL thường đại diện cho các tài nguyên có thể truy cập qua http. Tôi không nghĩ rằng có bất kỳ loại quy tắc không đồng bộ nào về việc khi nào thứ gì đó phải là URI so với URL. URI là các chuỗi ở dạng "giao thức: // resource-Identity" chẳng hạn như bitcoin: // params, http://something.com?param=value . Các lớp như URL thường bao bọc chuỗi và cung cấp các phương thức tiện ích mà Chuỗi sẽ không có lý do gì để cung cấp.

Không có thứ gì gọi là Tài nguyên, ít nhất là không theo nghĩa bạn đang nói đến. Chỉ vì một phương thức được đặt tên là getResource không có nghĩa là nó trả về một đối tượng kiểu Resource.

Cuối cùng, cách tốt nhất để tìm ra những gì các phương thức của Lớp làm là tạo một thể hiện của nó trong mã của bạn, gọi các phương thức và sau đó chuyển qua chế độ gỡ lỗi hoặc gửi kết quả đến System.out.


Định nghĩa của "con đường" không tương ứng với các khái niệm về "con đường" trong bối cảnh OP
leonbloy
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.