Je dois déterminer si une chaîne fournie par l'utilisateur est un chemin de fichier valide (c'est-à-dire si createNewFile () réussira ou lèvera une exception), mais je ne veux pas surcharger le système de fichiers avec des fichiers inutiles créés uniquement à des fins de validation. y at-il un moyen de déterminer si la chaîne que j’ai est un chemin de fichier valide sans essayer de créer le fichier?
Je sais que la définition de "chemin de fichier valide" varie en fonction du système d'exploitation, mais je me demandais s'il existait un moyen rapide d'accepter "C:/toto" ou "/ foo" et de rejeter "banane" ... une approche possible tente peut-être de créer le fichier et de le supprimer éventuellement si la création a réussi, mais j'espère qu'il existe un moyen plus élégant d'obtenir le même résultat ...
Cela vérifierait également l'existence du répertoire.
File file = new File("c:\\cygwin\\cygwin.bat");
if (!file.isDirectory())
file = file.getParentFile();
if (file.exists()){
...
}
Il semble que file.canWrite () ne vous indique pas clairement si vous avez le droit d'écrire dans le répertoire.
La classe de chemin introduite dans Java 7 ajoute de nouvelles alternatives, telles que:
(Est-ce que not ne fonctionne pas correctement sous Linux - renvoie toujours la valeur true)
/**
* <pre>
* Checks if a string is a valid path.
* Null safe.
*
* Calling examples:
* isValidPath("c:/test"); //returns true
* isValidPath("c:/te:t"); //returns false
* isValidPath("c:/te?t"); //returns false
* isValidPath("c/te*t"); //returns false
* isValidPath("good.txt"); //returns true
* isValidPath("not|good.txt"); //returns false
* isValidPath("not:good.txt"); //returns false
* </pre>
*/
public static boolean isValidPath(String path) {
try {
Paths.get(path);
} catch (InvalidPathException | NullPointerException ex) {
return false;
}
return true;
}
File.getCanonicalPath()
est très utile à cette fin. Des exceptions IO sont levées pour certains types de noms de fichiers non valides (par exemple, CON
, PRN
, *?*
sous Windows) lors de la résolution contre le système d'exploitation ou le système de fichiers. Cependant, cela ne sert qu’à titre de vérification préliminaire; vous devrez toujours gérer d’autres échecs lors de la création du fichier (autorisations insuffisantes, manque d’espace disque, restrictions de sécurité, par exemple).
Un certain nombre de problèmes peuvent survenir lorsque vous essayez de créer un fichier:
Plus précisément, ceux-ci peuvent changer entre le moment où vous essayez et la requête pour voir si vous pouvez et quand vous le pouvez réellement. Dans un environnement multithread, il s’agit d’une des causes principales de la concurrence et peut constituer une réelle vulnérabilité de certains programmes.
Fondamentalement, vous devez simplement essayer de le créer et voir si cela fonctionne. Et c'est la bonne façon de le faire. C'est pourquoi des éléments tels que ConcurrentHashMap
ont une putIfAbsent()
. Le contrôle et l'insertion sont donc des opérations atomiques et ne souffrent pas des conditions de concurrence. C'est exactement le même principe qui est en jeu ici.
Si cela fait simplement partie d'un processus de diagnostic ou d'installation, faites-le et voyez si cela fonctionne. Encore une fois, rien ne garantit que cela fonctionnera plus tard, cependant.
En gros, votre programme doit être suffisamment robuste pour disparaître gracieusement s'il ne peut pas écrire un fichier pertinent.
Voici quelque chose que vous pouvez faire qui fonctionne sur tous les systèmes d'exploitation
Utilisation de la correspondance régulière pour vérifier les caractères non valides existants.
if (newName.matches(".*[/\n\r\t\0\f`?*\\<>|\":].*")) {
System.out.println("Invalid!");
} else {
System.out.println("Valid!");
}
Avantages
Les inconvénients
Une approche possible peut être de tenter de créer le fichier et éventuellement de le supprimer si la création aboutit, mais j'espère qu'il existe un moyen plus élégant d'obtenir le même résultat.
Peut-être que c'est la manière la plus robuste.
Vous trouverez ci-dessous canCreateOrIsWritable
qui détermine si votre programme est capable de créer un fichier et ses répertoires parent sur un chemin donné, ou, s’il ya déjà un fichier, d’y écrire.
Pour ce faire, il crée en fait les répertoires parent nécessaires ainsi qu’un fichier vide sur le chemin. Ensuite, il les supprime (s’il existait un fichier dans le chemin, il restait seul).
Voici comment vous pourriez l'utiliser:
var myFile = new File("/home/me/maybe/write/here.log")
if (canCreateOrIsWritable(myFile)) {
// We're good. Create the file or append to it
createParents(myFile);
appendOrCreate(myFile, "new content");
} else {
// Let's pick another destination. Maybe the OS's temporary directory:
var tempDir = System.getProperty("Java.io.tmpdir");
var alternative = Paths.get(tempDir, "second_choice.log");
appendOrCreate(alternative, "new content in temporary directory");
}
La méthode essentielle avec quelques méthodes d'assistance:
static boolean canCreateOrIsWritable(File file) {
boolean canCreateOrIsWritable;
// The non-existent ancestor directories of the file.
// The file's parent directory is first
List<File> parentDirsToCreate = getParentDirsToCreate(file);
// Create the parent directories that don't exist, starting with the one
// highest up in the file system hierarchy (closest to root, farthest
// away from the file)
reverse(parentDirsToCreate).forEach(File::mkdir);
try {
boolean wasCreated = file.createNewFile();
if (wasCreated) {
canCreateOrIsWritable = true;
// Remove the file and its parent dirs that didn't exist before
file.delete();
parentDirsToCreate.forEach(File::delete);
} else {
// There was already a file at the path → Let's see if we can
// write to it
canCreateOrIsWritable = Java.nio.file.Files.isWritable(file.toPath());
}
} catch (IOException e) {
// File creation failed
canCreateOrIsWritable = false;
}
return canCreateOrIsWritable;
}
static List<File> getParentDirsToCreate(File file) {
var parentsToCreate = new ArrayList<File>();
File parent = file.getParentFile();
while (parent != null && !parent.exists()) {
parentsToCreate.add(parent);
parent = parent.getParentFile();
}
return parentsToCreate;
}
static <T> List<T> reverse(List<T> input) {
var reversed = new ArrayList<T>();
for (int i = input.size() - 1; i >= 0; i--) {
reversed.add(input.get(i));
}
return reversed;
}
static void createParents(File file) {
File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();
}
}
N'oubliez pas qu'entre l'appel canCreateOrIsWritable
et la création du fichier réel, le contenu et les autorisations de votre système de fichiers peuvent avoir changé.