Quelle est la différence entre une ressource, un URI, une URL, un chemin et un fichier en Java?

96

Je regarde un morceau de code Java en ce moment, et il prend un chemin sous forme de chaîne et obtient son URL en utilisant URL resource = ClassLoader.getSystemClassLoader().getResource(pathAsString);, puis appelle String path = resource.getPath()et enfin s'exécute new File(path);.

Oh, et il y a aussi des appels vers URL url = resource.toURI();et String file = resource.getFile().

Je suis totalement confus en ce moment - principalement à cause de la terminologie, je suppose. Quelqu'un peut-il s'il vous plaît me guider à travers les différences ou fournir quelques liens vers du matériel factice? Surtout URI vers URL et ressource vers fichier ? Pour moi, j'ai l'impression qu'ils devraient être la même chose, respectivement ...

La différence entre getFile()et getPath()est expliquée ici: Quelle est la différence entre url.getFile () et getpath ()? (Fait intéressant, ils semblent tous les deux renvoyer des chaînes, ce qui ajoute probablement beaucoup à mon état d'esprit ...)

Maintenant, si j'ai un localisateur qui fait référence à une classe ou à un package dans un fichier jar, ces deux (c'est-à-dire le chemin et les chaînes de fichiers) seront-ils différents?

resource.toString()vous donnerait jar:file:/C:/path/to/my.jar!/com/example/, après tout (notez le point d'exclamation).

La différence entre l' URI et l' URL en Java est-elle que le premier n'encode pas les espaces? Cf. Fichiers, URI et URL en conflit en Java (Cette réponse explique assez bien la différence conceptuelle générale entre les deux termes: les URI identifient et les URL localisent; )

Enfin - et surtout - pourquoi ai-je besoin d'un Fileobjet; pourquoi une Resource ( URL) n'est-elle pas suffisante? (Et y a-t-il un objet Resource?)

Désolé si cette question est un peu désorganisée; cela reflète juste la confusion que j'ai ... :)

Christian
la source
5
Et vous n'avez même pas commencé à regarder Pathet FileSystem de NIO :)
vérifie le
2
@eckes Un mal de tête à la fois, s'il vous plaît. ;)
Christian
1
Eh bien dans le contexte de votre question Fichier / URL + URI ne sont pas liés. L'un est un moyen de nommer et d'opérer sur des fichiers, l'autre est une méthode pour nommer et lire à partir de ressources (qui peuvent être des fichiers). Les méthodes getFile et getPath traitent les composants d'une URL qui sont nommés (de manière confuse) comme des objets fichier. Les ressources du chargeur de classe ne sont pas représentées sous forme de fichiers car elles peuvent avoir des origines différentes (ou être imbriquées dans des fichiers JAR).
eckes
1
Je note que ce code ne fonctionnera probablement pas comme prévu. A URLest opaque - comme vous le montrez, c'est-à jar:file:-dire une ressource dans une .jararchive. Il Fileest très peu probable que cela aboutisse à quelque chose d'utile.
Boris the Spider
1
Le cœur de votre problème est que les mots ressource et chemin peuvent avoir des significations différentes, selon le contexte.
Raedwald

Réponses:

43

MISE À JOUR 2017-04-12 Vérifiez la réponse de JvR car elle contient des explications plus exhaustives et exactes!


Veuillez noter que je ne me considère pas à 100% compétent pour répondre, mais voici néanmoins quelques commentaires:

  • File représente un fichier ou un répertoire accessible via le système de fichiers
  • ressource est un terme générique pour un objet de données qui peut être chargé par l'application
    • généralement les ressources sont des fichiers distribués avec l'application / la bibliothèque et chargés via un mécanisme de chargement de classe (quand ils résident sur le chemin de classe)
  • URL#getPathest getter sur la partie chemin de l'URL ( protocol://host/path?query)
  • URL#getFile selon les retours JavaDoc path+query

En Java, il URIs'agit simplement d'une structure de données pour manipuler l'identifiant générique lui-même.

URLd'autre part est vraiment un localisateur de ressources et vous offre des fonctionnalités pour lire réellement la ressource via des URLStreamHandlers enregistrés .

Les URL peuvent conduire à des ressources de système de fichiers et vous pouvez construire une URL pour chaque ressource de système de fichiers en utilisant le file://protocole (d'où la relation File<-> URL).

Sachez également que cela URL#getFilen'a aucun rapport avec java.io.File.


Pourquoi ai-je besoin d'un objet File; pourquoi une ressource (URL) n'est-elle pas suffisante?

C'est assez. Ce n'est que si vous souhaitez transmettre la ressource à un composant qui ne peut fonctionner qu'avec des fichiers, vous devez en tirer File. Cependant, toutes les URL de ressources ne peuvent pas être converties en Files.

Et y a-t-il un objet Resource?

Du point de vue JRE, ce n'est qu'un terme. Certains frameworks vous fournissent une telle classe (par exemple Spring's Resource ).

Pavel Horal
la source
5
Il y a aussi java.nio.file.Path, qui est essentiellement un remplacement (Java 7+) pour java.io.File, car cette dernière API était apparemment mal pensée au début de Java.
ntoskrnl
1
En règle générale, vous devez minimiser l'utilisation de l'URL sauf si cela est absolument nécessaire. La raison en est que les méthodes equals et hashCode de l'URL sont implémentées de manière surprenante: elles bloquent les appels de méthode.
kibibyte
3
@kibibyte: Je m'attendrais à ce que l'appel soit bloquant, d'avoir une implémentation asynchrone de hashcode et égal maintenant, ce serait très dérangeant. Je pense que ce que vous vouliez dire, c'est que les appels essaieront de résoudre l'hôte pour trouver s'ils sont équivalents et pourraient donc potentiellement bloquer les appels réseau.
Newtopian
50

Je suis totalement confus en ce moment - principalement à cause de la terminologie, je suppose. Quelqu'un peut-il s'il vous plaît me guider à travers les différences ou fournir quelques liens vers du matériel factice? Surtout URI vers URL et ressource vers fichier? Pour moi, j'ai l'impression qu'ils devraient être la même chose, respectivement ...

La terminologie est déroutante et parfois déroutante, et est principalement née de l'évolution de Java en tant qu'API et en tant que plate-forme au fil du temps. Pour comprendre comment ces termes ont fini par signifier ce qu'ils font, il est important de reconnaître deux choses qui influencent la conception de Java:

  • Rétrocompatibilité. Les anciennes applications doivent fonctionner sur des installations plus récentes, idéalement sans modification. Cela signifie qu'une ancienne API (avec ses noms et sa terminologie) doit être maintenue dans toutes les versions les plus récentes.
  • Multiplateforme. L'API doit fournir une abstraction utilisable de sa plate-forme sous-jacente, qu'il s'agisse d'un système d'exploitation ou d'un navigateur.

Je vais parcourir les concepts et comment ils sont nés. Je répondrai à vos autres questions précises après cela, car je devrai peut-être faire référence à quelque chose dans la première partie.

Qu'est-ce qu'une «ressource»?

Un élément de données abstrait et générique qui peut être localisé et lu. En clair, Java l'utilise pour désigner un «fichier» qui n'est peut-être pas un fichier mais qui représente une donnée nommée. Il n'a pas de représentation directe de classe ou d'interface en Java , mais en raison de ses propriétés (localisables, lisibles), il est souvent représenté par une URL.

Parce que l'un des premiers objectifs de conception de Java était d'être exécuté à l'intérieur d'un navigateur, en tant qu'application sandbox (applets!) Avec des droits / privilèges / habilitation de sécurité très limités, Java fait une différence claire (théorique) entre un fichier (quelque chose sur le système de fichiers) et une ressource (quelque chose qu'il doit lire). C'est pourquoi la lecture de quelque chose relatif à l'application (icônes, fichiers de classe, etc.) se fait via ClassLoader.getResourceet non via la classe File.

Malheureusement, parce que "ressource" est également un terme générique utile en dehors de cette interprétation, il est également utilisé pour nommer des choses très spécifiques (par exemple, classe ResourceBundle , UIResource , Resource ) qui ne sont pas, dans ce sens, une ressource.

Les principales classes représentant (un chemin vers) une ressource sont java.nio.file.Path , java.io.File , java.net.URI et java.net.URL .

Fichier (java.io, 1.0)

Une représentation abstraite des chemins de fichiers et de répertoires.

La classe File représente une ressource accessible via le système de fichiers natif de la plateforme . Il ne contient que le nom du fichier, il s'agit donc plutôt d'un chemin (voir plus loin) que la plateforme hôte interprète selon ses propres paramètres, règles et syntaxe.

Notez que File n'a pas besoin de pointer vers quelque chose de local , juste quelque chose que la plate-forme hôte comprend dans le contexte de l'accès aux fichiers, par exemple un chemin UNC dans Windows. Si vous montez un fichier ZIP en tant que système de fichiers dans votre système d'exploitation, File lira très bien ses entrées contenues.

URL (java.net, 1.0)

L'URL de classe représente un localisateur de ressources uniforme, un pointeur vers une «ressource» sur le World Wide Web. Une ressource peut être quelque chose d'aussi simple qu'un fichier ou un répertoire, ou il peut s'agir d'une référence à un objet plus complexe, comme une requête à une base de données ou à un moteur de recherche.

En tandem avec le concept de ressource, l'URL représente cette ressource de la même manière que la classe File représente un fichier dans la plate-forme hôte: sous la forme d' une chaîne structurée qui pointe vers une ressource. URL contient en plus un schéma qui indique comment atteindre la ressource (avec "file:" étant "demander à la plate-forme hôte"), et permet ainsi de pointer vers les ressources via HTTP, FTP, dans un JAR, etc.

Malheureusement, les URL ont leur propre syntaxe et terminologie, y compris l'utilisation de «fichier» et «chemin». Dans le cas où l'URL est une URL de fichier, URL.getFile renverra une chaîne identique à la chaîne de chemin du fichier référencé.

Class.getResource renvoie une URL: il est plus flexible que le renvoi de File, et il a répondu aux besoins du système comme imaginé au début des années 1990.

URI (java.net, 1.4)

Représente une référence URI (Uniform Resource Identifier).

L'URI est une (légère) abstraction sur l'URL. La différence entre l'URI et l'URL est conceptuelle et principalement académique, mais l'URI est mieux définie dans un sens formel et couvre un plus large éventail de cas d'utilisation. Parce qu'URL et URI sont / n'étaient pas la même chose, une nouvelle classe a été introduite pour les représenter, avec les méthodes URI.toURL et URL.toURI pour se déplacer entre l'une et l'autre.

En Java, la principale différence entre l'URL et l'URI est qu'une URL est censée être résoluble , ce dont l'application peut souhaiter un InputStream; un URI est traité plus comme un thingamajig abstrait qui pourrait pointer vers quelque chose de résoluble (et le fait généralement), mais ce que cela signifie et comment l'atteindre sont plus ouverts au contexte et à l'interprétation.

Chemin (java.nio.file, 1.7)

Un objet qui peut être utilisé pour localiser un fichier dans un système de fichiers. Il représentera généralement un chemin de fichier dépendant du système.

La nouvelle API de fichier, iconifiée dans l'interface Path, permet une flexibilité beaucoup plus grande que celle que la classe File pourrait offrir. L'interface Path est une abstraction de la classe File et fait partie de l' API New IO File . Où File pointe nécessairement vers un "fichier" tel que compris par la plateforme hôte, Path est plus générique: il représente un fichier (ressource) dans un système de fichiers arbitraire .

Path supprime la dépendance au concept de fichier de la plate-forme hôte. Il peut s'agir d'une entrée dans un fichier ZIP, d'un fichier accessible via FTP ou SSH-FS, une représentation multi-racine du chemin de classe de l'application, ou vraiment tout ce qui peut être représenté de manière significative via l'interface FileSystem et son pilote, FileSystemProvider. Il apporte la puissance de "monter" des systèmes de fichiers dans le contexte d'une application Java.

La plate-forme hôte est représentée par le "système de fichiers par défaut"; lorsque vous appelez File.toPath, vous obtenez un chemin sur le système de fichiers par défaut.


Maintenant, si j'ai un localisateur qui fait référence à une classe ou à un package dans un fichier jar, ces deux (c'est-à-dire le chemin et les chaînes de fichiers) seront-ils différents?

Improbable. Si le fichier jar est sur le système de fichiers local, vous ne devriez pas avoir un composant de requête, donc URL.getPathet URL.getFiledoit retourner le même résultat. Cependant, choisissez celui dont vous avez besoin: les URL de fichiers peuvent généralement ne pas avoir de composants de requête, mais je pourrais certainement en ajouter un de toute façon.

Enfin - et surtout - pourquoi ai-je besoin d'un objet File; pourquoi une ressource (URL) n'est-elle pas suffisante?

L'URL peut ne pas être suffisante car File vous donne accès à des données de gestion telles que les autorisations (lisibles, inscriptibles, exécutables), le type de fichier (suis-je un répertoire?), Et la possibilité de rechercher et de manipuler le système de fichiers local. Si ce sont des fonctionnalités dont vous avez besoin, File ou Path les fournit.

Vous n'avez pas besoin de File si vous avez accès à Path. Cependant, certaines API plus anciennes peuvent nécessiter un fichier.

(Et y a-t-il un objet Resource?)

Non, il n'y en a pas. Il y a beaucoup de choses nommées comme ça, mais elles ne sont pas une ressource au sens de ClassLoader.getResource.

JvR
la source
Wow, très complet. Juste en train de le parcourir, mais vous avez déjà la première question de suivi: lorsque vous dites qu'un fichier "contient uniquement le nom du fichier", ne contredisez pas votre déclaration initiale selon laquelle il s'agit d'une "représentation abstraite des chemins de fichiers et de répertoires" - iemore?
Christian
1
@Christian Je voulais dire "seulement le nom" comme dans: ne modélise en aucun cas le contenu du fichier; c'est simplement une fine enveloppe autour d'une ficelle. La partie "représentation abstraite" est citée dans la documentation de l'API. ;)
JvR
Cette réponse mérite d'avoir beaucoup plus de votes positifs ... mettra à jour ma réponse acceptée pour diriger les lecteurs vers celle-ci.
Pavel Horal
12

La réponse de Pavel Horal est sympa.

Comme il le dit, le mot «fichier» a des significations totalement différentes (pratiquement sans rapport) dans URL#getFilevs java.io.File- peut-être que cela fait partie de la confusion.

Juste pour ajouter:

  • Une ressource en Java est un concept abstrait, une source de données qui peuvent être lues. L'emplacement (ou l'adresse) d'une ressource est représenté en Java par un URLobjet.

  • Une ressource peut correspondre à un fichier normal dans le système de fichiers local (en particulier, quand il URLcommence par file://). Mais une ressource est plus générale (il peut s'agir aussi d'un fichier stocké dans un jar, ou de certaines données à lire depuis le réseau, ou depuis la mémoire, ou ...). Et c'est aussi plus limité, car un File(en plus d'être autre chose qu'un fichier normal: un répertoire, un lien) peut également être créé et écrit.

  • Rappelez-vous qu'en Java, un Fileobjet ne représente pas vraiment "un fichier" mais l'emplacement (le nom complet, avec chemin) d'un fichier. Ainsi, un Fileobjet vous permet de localiser (et d'ouvrir) un fichier, tout comme un URLvous permet d'accéder (et d'ouvrir) une ressource. (Il n'y a pas de Resourceclasse en Java pour représenter une ressource, mais il n'y en a pas non plus pour représenter un fichier! Encore une fois: ce Filen'est pas un fichier, c'est le chemin d'un fichier).

Leonbloy
la source
3

Si je les comprends bien, vous pouvez les classer comme suit:

Basé sur le Web: URI et URL.

  • URL: une URL est un emplacement défini sur l'internt (juste une adresse Web normale comme - stackoverflow.com)
  • URI: Ever URL est un URI. Mais les URI peuvent aussi contenir des choses comme "mailto:", donc ils sont aussi, enfin, un "script" je dirais.

Et local: ressource, chemin et fichiers

  • Ressource: les ressources sont des fichiers dans votre fichier jar. Ils sont utilisés pour charger des fichiers à partir de jars / conteneurs.
  • Chemin: un chemin est essentiellement une chaîne. Mais il est livré avec des fonctions pratiques pour concaténer plusieurs chaînes ou ajouter des fichiers à une chaîne. Il s'assure que le chemin que vous construisez est valide.
  • Fichier: il s'agit d'une référence à un répertoire ou un fichier. Il est utilisé pour modifier les fichiers, les ouvrir, etc.

Ce serait plus facile si elles étaient fusionnées en une seule classe - elles sont vraiment déroutantes: D

J'espère que ceci vous aide :)

(Je viens de jeter un œil à la documentation - regardez docs.oracle.com)

Cyphrags
la source
0

Un fichier est une représentation abstraite d'une entité dans le système de fichiers local.

Un chemin est généralement une chaîne indiquant l'emplacement d'un fichier dans un système de fichiers. Il n'inclut généralement pas le nom de fichier. Donc c: \ documents \ mystuff \ stuff.txt aurait un chemin avec la valeur de "C: \ documents \ mystuff" Evidemment, le format des noms de fichiers et des chemins absolus varierait énormément d'un système de fichiers à un autre.

URL est un sous-ensemble d'URI avec une URL représentant généralement des ressources accessibles via http. Je ne pense pas qu'il y ait une sorte de règle absolue sur le moment où quelque chose doit être un URI par rapport à une URL. Les URI sont des chaînes sous la forme "protocol: // resource-identifier" comme bitcoin: // params, http://something.com?param=value . Les classes comme URL enveloppent généralement la chaîne et fournissent des méthodes utilitaires que String n'aurait aucune raison de fournir.

Rien de tel que Resource, du moins pas dans le sens dont vous parlez. Ce n'est pas parce qu'une méthode est nommée getResource qu'elle renvoie un objet de type Resource.

En fin de compte, la meilleure façon de comprendre ce que font les méthodes d'une classe est de créer une instance de celle-ci dans votre code, d'appeler les méthodes, puis de passer en mode débogage ou d'envoyer les résultats à System.out.

Jim W
la source
Votre définition de "chemin" ne correspond PAS au concept de "chemin" dans le contexte OP
leonbloy