web-dev-qa-db-fra.com

Manière concise standard de copier un fichier en Java?

Cela m'a toujours dérangé que le seul moyen de copier un fichier dans Java implique d'ouvrir des flux, de déclarer un tampon, de lire un fichier, de le parcourir en boucle et de l'écrire sur l'autre Steam. Le Web est jonché d'implémentations similaires mais néanmoins légèrement différentes de ce type de solution.

Existe-t-il un meilleur moyen de rester dans les limites du langage Java (sa signification n'implique pas l'exécution de commandes spécifiques à un système d'exploitation)? Peut-être que dans un package d’utilitaire open source fiable, cela masquerait au moins cette implémentation sous-jacente et fournirait une solution d’une ligne?

416
Peter

Comme le kit d'outils le mentionne ci-dessus, Apache Commons IO est le chemin à parcourir, en particulier FileUtils . copyFile () ; il gère tout le travail lourd pour vous.

Et en tant que postscript, notez que les versions récentes de FileUtils (telles que la version 2.0.1) ont ajouté l’utilisation de NIO pour la copie de fichiers; NIO peut augmenter considérablement les performances de copie de fichiers , en grande partie parce que les routines NIO reportent la copie directement sur le système d'exploitation/système de fichiers plutôt que de le gérer en lisant et en écrivant des octets via le Java couche. Donc, si vous recherchez des performances, il peut être intéressant de vérifier que vous utilisez une version récente de FileUtils.

272
delfuego

Je voudrais éviter l'utilisation d'un méga api comme Apache commons. Il s’agit d’une opération simpliste intégrée au kit JDK du nouveau package NIO. C'était déjà lié à une réponse précédente, mais la méthode clé de l'API NIO sont les nouvelles fonctions "transferTo" et "transferFrom".

http://Java.Sun.com/javase/6/docs/api/Java/nio/channels/FileChannel.html#transferTo (long,% 20long,% 20Java.nio.channels.WritableByteChannel) =

Un des articles liés montre comment intégrer cette fonction dans votre code, en utilisant le transferFrom:

public static void copyFile(File sourceFile, File destFile) throws IOException {
    if(!destFile.exists()) {
        destFile.createNewFile();
    }

    FileChannel source = null;
    FileChannel destination = null;

    try {
        source = new FileInputStream(sourceFile).getChannel();
        destination = new FileOutputStream(destFile).getChannel();
        destination.transferFrom(source, 0, source.size());
    }
    finally {
        if(source != null) {
            source.close();
        }
        if(destination != null) {
            destination.close();
        }
    }
}

Apprendre NIO peut être un peu délicat, alors vous voudrez peut-être faire confiance à ce mécanisme avant de partir et d’essayer d’apprendre NIO du jour au lendemain. Par expérience personnelle, il peut être très difficile d’apprendre si vous n’avez pas cette expérience et si vous avez été initié à IO via les flux Java.io.

276
Josh

Maintenant, avec Java 7, vous pouvez utiliser la syntaxe try-with-resource suivante:

public static void copyFile( File from, File to ) throws IOException {

    if ( !to.exists() ) { to.createNewFile(); }

    try (
        FileChannel in = new FileInputStream( from ).getChannel();
        FileChannel out = new FileOutputStream( to ).getChannel() ) {

        out.transferFrom( in, 0, in.size() );
    }
}

Ou, mieux encore, cela peut également être accompli en utilisant la nouvelle classe Files introduite dans Java 7:

public static void copyFile( File from, File to ) throws IOException {
    Files.copy( from.toPath(), to.toPath() );
}

Plutôt chic, hein?

179
Scott
  • Ces méthodes sont conçues pour la performance (elles s’intègrent aux E/S natives du système d’exploitation).
  • Ces méthodes fonctionnent avec des fichiers, des répertoires et des liens.
  • Chacune des options fournies peut être omise - elles sont facultatives.

La classe utilitaire

package com.yourcompany.nio;

class Files {

    static int copyRecursive(Path source, Path target, boolean Prompt, CopyOptions options...) {
        CopyVisitor copyVisitor = new CopyVisitor(source, target, options).copy();
        EnumSet<FileVisitOption> fileVisitOpts;
        if (Arrays.toList(options).contains(Java.nio.file.LinkOption.NOFOLLOW_LINKS) {
            fileVisitOpts = EnumSet.noneOf(FileVisitOption.class) 
        } else {
            fileVisitOpts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
        }
        Files.walkFileTree(source[i], fileVisitOpts, Integer.MAX_VALUE, copyVisitor);
    }

    private class CopyVisitor implements FileVisitor<Path>  {
        final Path source;
        final Path target;
        final CopyOptions[] options;

        CopyVisitor(Path source, Path target, CopyOptions options...) {
             this.source = source;  this.target = target;  this.options = options;
        };

        @Override
        FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
        // before visiting entries in a directory we copy the directory
        // (okay if directory already exists).
        Path newdir = target.resolve(source.relativize(dir));
        try {
            Files.copy(dir, newdir, options);
        } catch (FileAlreadyExistsException x) {
            // ignore
        } catch (IOException x) {
            System.err.format("Unable to create: %s: %s%n", newdir, x);
            return SKIP_SUBTREE;
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
        Path newfile= target.resolve(source.relativize(file));
        try {
            Files.copy(file, newfile, options);
        } catch (IOException x) {
            System.err.format("Unable to copy: %s: %s%n", source, x);
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
        // fix up modification time of directory when done
        if (exc == null && Arrays.toList(options).contains(COPY_ATTRIBUTES)) {
            Path newdir = target.resolve(source.relativize(dir));
            try {
                FileTime time = Files.getLastModifiedTime(dir);
                Files.setLastModifiedTime(newdir, time);
            } catch (IOException x) {
                System.err.format("Unable to copy all attributes to: %s: %s%n", newdir, x);
            }
        }
        return CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) {
        if (exc instanceof FileSystemLoopException) {
            System.err.println("cycle detected: " + file);
        } else {
            System.err.format("Unable to copy: %s: %s%n", file, exc);
        }
        return CONTINUE;
    }
}

Copier un répertoire ou un fichier

long bytes = Java.nio.file.Files.copy( 
                 new Java.io.File("<filepath1>").toPath(), 
                 new Java.io.File("<filepath2>").toPath(),
                 Java.nio.file.StandardCopyOption.REPLACE_EXISTING,
                 Java.nio.file.StandardCopyOption.COPY_ATTRIBUTES,
                 Java.nio.file.LinkOption.NOFOLLOW_LINKS);

Déplacer un répertoire ou un fichier

long bytes = Java.nio.file.Files.move( 
                 new Java.io.File("<filepath1>").toPath(), 
                 new Java.io.File("<filepath2>").toPath(),
                 Java.nio.file.StandardCopyOption.ATOMIC_MOVE,
                 Java.nio.file.StandardCopyOption.REPLACE_EXISTING);

Copier un répertoire ou un fichier de manière récursive

long bytes = com.yourcompany.nio.Files.copyRecursive( 
                 new Java.io.File("<filepath1>").toPath(), 
                 new Java.io.File("<filepath2>").toPath(),
                 Java.nio.file.StandardCopyOption.REPLACE_EXISTING,
                 Java.nio.file.StandardCopyOption.COPY_ATTRIBUTES
                 Java.nio.file.LinkOption.NOFOLLOW_LINKS );
87
Glen Best

En Java 7, c'est facile ...

File src = new File("original.txt");
File target = new File("copy.txt");

Files.copy(src.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
39
Kevin Sadler

Pour copier un fichier et le sauvegarder dans votre chemin de destination, vous pouvez utiliser la méthode ci-dessous.

public void copy(File src, File dst) throws IOException {
    InputStream in = new FileInputStream(src);
    try {
        OutputStream out = new FileOutputStream(dst);
        try {
            // Transfer bytes from in to out
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
}
28
Rakshi

Notez que tous ces mécanismes ne copient que le contenu du fichier, pas les métadonnées telles que les autorisations. Donc, si vous deviez copier ou déplacer un fichier .sh exécutable sur linux, le nouveau fichier ne serait pas exécutable.

Pour vraiment copier ou déplacer un fichier, c'est-à-dire obtenir le même résultat que copier depuis une ligne de commande, vous devez utiliser un outil natif. Soit un script shell ou JNI.

Apparemment, cela pourrait être corrigé dans Java 7 - http://today.Java.net/pub/a/today/2008/07/03/jsr-203-new-file- apis.html . Doigts croisés!

24
Brad at Kademi

La bibliothèque Google de goyave a également une méthode de copie :

Vide public statique copie( Fichier  de, 
  Fichier  à) 
 Lance  IOException
Copie tous les octets d’un fichier à un autre.

Attention: Si to représente un fichier existant, ce fichier sera écrasé par le contenu de from. Si to et from se rapportent à la même fichier, le contenu de ce fichier sera supprimé.

Paramètres:from - le fichier sourceto - le fichier de destination

Jette: IOException - si une erreur d'E/S se produit IllegalArgumentException - si from.equals(to)

</ dd>
22
Andrew McKinlay

Disponible en standard dans Java 7, path.copyTo: http://openjdk.Java.net/projects/nio/javadoc/Java/nio/file/Path.html - http://Java.Sun.com/docs/books/tutorial/essential/io/copy.html

Je ne peux pas croire qu'il leur a fallu si longtemps pour normaliser quelque chose d'aussi courant et simple que la copie de fichiers :(

19
Ryan

Trois problèmes possibles avec le code ci-dessus:

  1. Si getChannel lève une exception, vous pourriez perdre un flux ouvert.
  2. Pour les fichiers volumineux, vous tentez peut-être de transférer davantage à la fois que le système d'exploitation ne peut en gérer.
  3. Vous ignorez la valeur de retour de transferFrom. Il ne s'agit donc peut-être que d'une copie du fichier.

C'est pourquoi org.Apache.tools.ant.util.ResourceUtils.copyResource est si compliqué. Notez également que bien que transferFrom soit correct, transferTo se bloque sur JDK 1.4 sous Linux (voir Bug ID: 5056395 ) - Jesse Glick Jan

7
saji

Voici trois manières de copier facilement des fichiers avec une seule ligne de code!

Java7 :

Java.nio.file.Files # copy

private static void copyFileUsingJava7Files(File source, File dest) throws IOException {
    Files.copy(source.toPath(), dest.toPath());
}

Appache Commons IO :

FileUtils # copyFile

private static void copyFileUsingApacheCommonsIO(File source, File dest) throws IOException {
    FileUtils.copyFile(source, dest);
}

Goyave :

Fichiers # copie

private static void copyFileUsingGuava(File source,File dest) throws IOException{
    Files.copy(source,dest);          
}
7
Jaskey

Si vous vous trouvez dans une application Web qui utilise déjà Spring et si vous ne souhaitez pas inclure Apache Commons IO pour la copie de fichiers simple, vous pouvez utiliser FileCopyUtils du framework Spring.

7
Balaji Paulrajan
public static void copyFile(File src, File dst) throws IOException
{
    long p = 0, dp, size;
    FileChannel in = null, out = null;

    try
    {
        if (!dst.exists()) dst.createNewFile();

        in = new FileInputStream(src).getChannel();
        out = new FileOutputStream(dst).getChannel();
        size = in.size();

        while ((dp = out.transferFrom(in, p, size)) > 0)
        {
            p += dp;
        }
    }
    finally {
        try
        {
            if (out != null) out.close();
        }
        finally {
            if (in != null) in.close();
        }
    }
}
6
user3200607

La copie NIO avec un tampon est la plus rapide selon mon test. Voir le code de travail ci-dessous à partir d'un projet test de ma mine à https://github.com/mhisoft/fastcopy

import Java.io.Closeable;
import Java.io.File;
import Java.io.FileInputStream;
import Java.io.FileOutputStream;
import Java.io.IOException;
import Java.nio.ByteBuffer;
import Java.nio.channels.FileChannel;
import Java.text.DecimalFormat;


public class test {

private static final int BUFFER = 4096*16;
static final DecimalFormat df = new DecimalFormat("#,###.##");
public static void nioBufferCopy(final File source, final File target )  {
    FileChannel in = null;
    FileChannel out = null;
    double  size=0;
    long overallT1 =  System.currentTimeMillis();

    try {
        in = new FileInputStream(source).getChannel();
        out = new FileOutputStream(target).getChannel();
        size = in.size();
        double size2InKB = size / 1024 ;
        ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER);

        while (in.read(buffer) != -1) {
            buffer.flip();

            while(buffer.hasRemaining()){
                out.write(buffer);
            }

            buffer.clear();
        }
        long overallT2 =  System.currentTimeMillis();
        System.out.println(String.format("Copied %s KB in %s millisecs", df.format(size2InKB),  (overallT2 - overallT1)));
    }
    catch (IOException e) {
        e.printStackTrace();
    }

    finally {
        close(in);
        close(out);
    }
}

private static void close(Closeable closable)  {
    if (closable != null) {
        try {
            closable.close();
        } catch (IOException e) {
            if (FastCopy.debug)
                e.printStackTrace();
        }    
    }
}

}

3
Tony

Rapide et fonctionne avec toutes les versions de Java également Android:

private void copy(final File f1, final File f2) throws IOException {
    f2.createNewFile();

    final RandomAccessFile file1 = new RandomAccessFile(f1, "r");
    final RandomAccessFile file2 = new RandomAccessFile(f2, "rw");

    file2.getChannel().write(file1.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, f1.length()));

    file1.close();
    file2.close();
}
2
user1079877

Un peu tard pour le parti, mais voici une comparaison du temps nécessaire pour copier un fichier à l’aide de diverses méthodes de copie. J'ai parcouru les méthodes 10 fois et pris une moyenne. Le transfert de fichier utilisant les flux IO semble être le pire candidat:

Comparison of file transfer using various methods

Voici les méthodes:

private static long fileCopyUsingFileStreams(File fileToCopy, File newFile) throws IOException {
    FileInputStream input = new FileInputStream(fileToCopy);
    FileOutputStream output = new FileOutputStream(newFile);
    byte[] buf = new byte[1024];
    int bytesRead;
    long start = System.currentTimeMillis();
    while ((bytesRead = input.read(buf)) > 0)
    {
        output.write(buf, 0, bytesRead);
    }
    long end = System.currentTimeMillis();

    input.close();
    output.close();

    return (end-start);
}

private static long fileCopyUsingNIOChannelClass(File fileToCopy, File newFile) throws IOException
{
    FileInputStream inputStream = new FileInputStream(fileToCopy);
    FileChannel inChannel = inputStream.getChannel();

    FileOutputStream outputStream = new FileOutputStream(newFile);
    FileChannel outChannel = outputStream.getChannel();

    long start = System.currentTimeMillis();
    inChannel.transferTo(0, fileToCopy.length(), outChannel);
    long end = System.currentTimeMillis();

    inputStream.close();
    outputStream.close();

    return (end-start);
}

private static long fileCopyUsingApacheCommons(File fileToCopy, File newFile) throws IOException
{
    long start = System.currentTimeMillis();
    FileUtils.copyFile(fileToCopy, newFile);
    long end = System.currentTimeMillis();
    return (end-start);
}

private static long fileCopyUsingNIOFilesClass(File fileToCopy, File newFile) throws IOException
{
    Path source = Paths.get(fileToCopy.getPath());
    Path destination = Paths.get(newFile.getPath());
    long start = System.currentTimeMillis();
    Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING);
    long end = System.currentTimeMillis();

    return (end-start);
}

Le seul inconvénient de l'utilisation de la classe de canaux NIO est que je n'arrive toujours pas à trouver le moyen de montrer la progression de la copie d'un fichier intermédiaire.

1
Vinit Shandilya