97 votes

Une alternative fiable à File.renameTo() sous Windows ?

Java File.renameTo() est problématique, surtout sous Windows, semble-t-il. Comme le Documentation de l'API dit,

De nombreux aspects du comportement de cette méthode sont intrinsèquement dépendent de la plate-forme : L'opération renommer peut ne pas être en mesure de déplacer un fichier d'un système de fichiers à un autre, elle pourrait ne pas être atomique, et elle pourrait ne pas réussir si un fichier avec le chemin d'accès abstrait de destination existe déjà existe déjà. La valeur de retour doit toujours être être vérifiée pour s'assurer que l'opération de l'opération de renommage a réussi.

Dans mon cas, dans le cadre d'une procédure de mise à jour, je dois déplacer (renommer) un répertoire qui peut contenir des gigaoctets de données (beaucoup de sous-répertoires et de fichiers de tailles différentes). Le déplacement se fait toujours au sein de la même partition/du même disque, il n'est donc pas nécessaire de déplacer physiquement tous les fichiers sur le disque.

Il y a ne devrait pas être n'importe quel verrou de fichier au contenu du répertoire à déplacer, mais quand même, assez souvent, renameTo() échoue à faire son travail et retourne false. (Je suppose simplement que peut-être certains verrous de fichiers expirent de manière quelque peu arbitraire sous Windows).

Actuellement, j'ai une méthode de secours qui utilise la copie et la suppression, mais c'est nul parce que cela peut prendre beaucoup de temps, en fonction de la taille du dossier. J'envisage également de documenter simplement le fait que l'utilisateur peut déplacer le dossier manuellement pour éviter d'attendre pendant des heures, potentiellement. Mais la bonne méthode serait évidemment quelque chose d'automatique et de rapide.

Donc ma question est, Connaissez-vous une approche alternative et fiable pour effectuer un déplacement/renommage rapide avec Java sous Windows ? soit avec le simple JDK ou une bibliothèque externe. Ou si vous connaissez un facile moyen de détecter et de lever tout verrouillage de fichier pour un dossier donné et tout son contenu (éventuellement des milliers de fichiers individuels), cela conviendrait également.


Editar : Dans ce cas particulier, il semble que nous nous en soyons sortis en utilisant juste renameTo() en tenant compte de quelques éléments supplémentaires ; voir cette réponse .

3 votes

Vous pouvez attendre/utiliser le JDK 7, qui prend bien mieux en charge les systèmes de fichiers.

0 votes

@kd304, en fait je ne peux pas attendre ou utiliser une version en accès anticipé, mais c'est intéressant de savoir que quelque chose comme ça est en route !

55voto

Alan Points 1112

Voir également le Files.move() dans le JDK 7.

Un exemple :

String fileName = "MyFile.txt";

try {
    Files.move(new File(fileName).toPath(), new File(fileName).toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
    Logger.getLogger(SomeClass.class.getName()).log(Level.SEVERE, null, ex);
}

10 votes

Malheureusement, Java7 n'est pas toujours la solution (comme 42).

1 votes

Même sous ubuntu, JDK7, nous avons rencontré ce problème lors de l'exécution du code sur EC2 avec le stockage EBS. File.renameTo a échoué ainsi que File.canWrite.

0 votes

Notez que cette méthode est aussi peu fiable que File#renameTo(). Elle donne simplement une erreur plus utile quand elle échoue. La seule façon raisonnablement fiable que j'ai trouvée est de copier le fichier avec Files#copy vers le nouveau nom, et ensuite de supprimer l'original en utilisant Files#delete (la suppression elle-même peut échouer aussi, pour la même raison que Files#move peut échouer).

27voto

Jonik Points 18905

Pour ce que ça vaut, quelques notions supplémentaires :

  1. Sous Windows, renameTo() semble échouer si le répertoire cible existe, même s'il est vide. Cela m'a surpris, car j'avais essayé sous Linux, où renameTo() a réussi si la cible existait, tant qu'elle était vide.

    (Évidemment, je n'aurais pas dû supposer que ce genre de chose fonctionne de la même manière sur toutes les plateformes ; c'est exactement ce contre quoi la Javadoc met en garde).

  2. Si vous pensez qu'il peut y avoir des verrouillages de fichiers persistants, attendez un peu avant le déplacement/renommage. pourrait aide. (À un moment donné, dans notre programme d'installation/de mise à niveau, nous avons ajouté une action de "veille" et une barre de progression indéterminée pendant environ 10 secondes, parce qu'un service pouvait s'accrocher à certains fichiers). Vous pourriez même mettre en place un mécanisme de relance simple qui essaierait de renameTo() puis attend pendant un certain temps (qui peut augmenter progressivement), jusqu'à ce que l'opération réussisse ou qu'un certain délai soit atteint.

Dans mon cas, la plupart des problèmes semblent avoir été résolus en tenant compte des deux éléments ci-dessus, de sorte que nous n'aurons pas besoin de faire un appel au noyau natif, ou quelque chose du genre, après tout.

2 votes

J'accepte ma propre réponse pour l'instant, car elle décrit ce qui a aidé dans notre cas. Cependant, si quelqu'un trouve une bonne réponse au problème plus général de renameTo(), n'hésitez pas à poster et je serai heureux de reconsidérer la réponse acceptée.

4 votes

6,5 ans plus tard, je pense qu'il est temps d'accepter la Réponse de JDK 7 à la place, d'autant plus que tant de personnes le considèrent comme utile. =)

19voto

MykennaC Points 414

Le message original demandait "une approche alternative et fiable pour effectuer un déplacement/renommage rapide avec Java sous Windows, soit avec le JDK classique, soit avec une bibliothèque externe".

Une autre option qui n'a pas encore été mentionnée ici est la v1.3.2 ou une version ultérieure de l'application apache.commons.io qui comprend FileUtils.moveFile() .

Il lance une IOException au lieu de renvoyer le booléen false en cas d'erreur.

Voir aussi gros bonnet La réponse de la Commission dans cet autre fil .

2 votes

De plus, il semble que le JDK 1.7 inclura une meilleure prise en charge des E/S du système de fichiers. Consultez java.nio.file.Path.moveTo() : java.sun.com/javase/7/docs/api/java/nio/file/Path.html

2 votes

Le JDK 1.7 n'a pas de méthode java.nio.file.Path.moveTo()

4voto

crazy horse Points 195

Le code suivant n'est PAS une "alternative" mais il a fonctionné de manière fiable pour moi sur les environnements Windows et Linux :

public static void renameFile(String oldName, String newName) throws IOException {
    File srcFile = new File(oldName);
    boolean bSucceeded = false;
    try {
        File destFile = new File(newName);
        if (destFile.exists()) {
            if (!destFile.delete()) {
                throw new IOException(oldName + " was not successfully renamed to " + newName); 
            }
        }
        if (!srcFile.renameTo(destFile))        {
            throw new IOException(oldName + " was not successfully renamed to " + newName);
        } else {
                bSucceeded = true;
        }
    } finally {
          if (bSucceeded) {
                srcFile.delete();
          }
    }
}

2 votes

Hmm, ce code supprime srcFile même si renameTo (ou destFile.delete) échoue et que la méthode lève une IOException ; je ne suis pas sûr que ce soit une bonne idée.

1 votes

@Jonik, Thanx, j'ai corrigé le code pour ne pas supprimer le fichier src si le renommage échoue.

0 votes

Merci pour ce partage, cela a résolu mon problème de renommage sous Windows.

4voto

Kactus Points 78

Je sais que cela semble un peu compliqué, mais pour ce dont j'ai besoin, il semble que les lecteurs et écrivains en mémoire tampon n'ont aucun problème pour créer les fichiers.

void renameFiles(String oldName, String newName)
{
    String sCurrentLine = "";

    try
    {
        BufferedReader br = new BufferedReader(new FileReader(oldName));
        BufferedWriter bw = new BufferedWriter(new FileWriter(newName));

        while ((sCurrentLine = br.readLine()) != null)
        {
            bw.write(sCurrentLine);
            bw.newLine();
        }

        br.close();
        bw.close();

        File org = new File(oldName);
        org.delete();

    }
    catch (FileNotFoundException e)
    {
        e.printStackTrace();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

}

Fonctionne bien pour les petits fichiers texte dans le cadre d'un analyseur, assurez-vous simplement que oldName et newName sont des chemins complets vers l'emplacement des fichiers.

Salutations Kactus

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X