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?
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.
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".
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.
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?
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;
}
}
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);
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);
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 . Doigts croisés!
La bibliothèque Google de goyave a également une méthode de copie :
Vide public statique copie( Fichier de, Fichier à) Lance IOException
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)
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 :(
Trois problèmes possibles avec le code ci-dessus:
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
Voici trois manières de copier facilement des fichiers avec une seule ligne de code!
Java7 :
private static void copyFileUsingJava7Files(File source, File dest) throws IOException {
Files.copy(source.toPath(), dest.toPath());
}
Appache Commons IO :
private static void copyFileUsingApacheCommonsIO(File source, File dest) throws IOException {
FileUtils.copyFile(source, dest);
}
Goyave :
private static void copyFileUsingGuava(File source,File dest) throws IOException{
Files.copy(source,dest);
}
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.
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. 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();
}
}
}
}
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();
}
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:
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.