Question

Cela m'a toujours dérangé de constater que le seul moyen de copier un fichier en Java consiste à ouvrir des flux, à déclarer un tampon, à lire un fichier, à le parcourir en boucle et à l'écrire dans l'autre sens. 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 au 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 à une ligne?

Était-ce utile?

La solution

Comme mentionné dans la boîte à outils ci-dessus, Apache Commons IO est la voie à suivre, en particulier FileUtils . copyFile () ; il gère tout le travail lourd pour vous.

En tant que post-scriptum, 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 grâce aux routines NIO. reporter la copie directement sur le système d'exploitation / système de fichiers plutôt que de la gérer en lisant et en écrivant des octets via la couche Java. Donc, si vous recherchez des performances, il peut être intéressant de vérifier que vous utilisez une version récente de FileUtils.

Autres conseils

J'éviterais 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)

L'un des articles liés montre comment intégrer cette fonction dans votre code en utilisant le paramètre 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, vous pouvez donc vous fier à 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.

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 réalisé à l'aide de la nouvelle classe Files introduite dans Java 7:

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

Assez chic, hein?

  • 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 d'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éplacement d'un répertoire ou d'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 );

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);

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();
    }
}

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 . Croisons les doigts!

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

public static void copy(File from,
                        File to)
                 throws IOException
Copie tous les octets d'un fichier à un autre.

Avertissement: Si à représente un fichier existant, ce fichier  sera écrasé par le contenu de à partir de . Si à et   à partir de fait référence au fichier identique , le contenu de ce fichier  sera supprimé.

Paramètres: de - le fichier source à - le fichier de destination

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

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 n'arrive pas à croire qu'il leur ait fallu si longtemps pour normaliser quelque chose d'aussi commun et simple que la copie de fichiers: (

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

  1. Si getChannel lève une exception, vous risquez de perdre un flux ouvert.
  2. Pour les fichiers volumineux, vous essayez peut-être de transférer plus à 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 de copier une partie 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 ID de bogue: 5056395 ) - Jesse Glick Jan

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.

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());
}

IO Appache Commons :

FileUtils # copyFile

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

Goyave :

Fichiers # copier

private static void copyFileUsingGuava(File source,File dest) throws IOException{
    Files.copy(source,dest);          
}
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();
        }
    }
}

La copie NIO avec un tampon est la plus rapide selon mon test. Consultez le code de travail ci-dessous tiré de l'un de mes projets tests à l'adresse 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();
        }    
    }
}

}

Rapide et fonctionne avec toutes les versions de Java et 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();
}

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 fichiers à l'aide de flux d'E / S semble être le pire candidat:

 Comparaison du transfert de fichiers à l’aide de diverses méthodes

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top