标准简洁的方式来复制在Java文件?
它一直困扰着我,用Java复制文件的唯一方法就是打开stream,声明一个缓冲区,读入一个文件,循环遍历它,然后把它写出来。 networking上散布着类似的,但这种types的解决scheme仍然稍有不同的实现。
有没有更好的方法,保持在Java语言的范围内(意思是不涉及执行特定于操作系统的命令)? 也许在一些可靠的开源工具包中,至less会掩盖这个底层的实现,并提供一个单一的解决scheme?
正如上面提到的工具包一样,Apache Commons IO是最好的select,特别是FileUtils 。 copyFile() ; 它为您处理所有繁重的工作。
作为后记,请注意最近版本的FileUtils(如2.0.1版本)已经添加了使用NIO来复制文件; NIO可以显着提高文件复制性能 ,很大程度上是因为NIO例程推迟直接复制到OS /文件系统,而不是通过Java层读写字节来处理它。 所以,如果你正在寻找性能,可能值得检查你使用的是最新版本的FileUtils。
我会避免使用像apache commons这样的大型api。 这是一个简单的操作,它在新的NIO包中embedded到JDK中。 这在前面的回答中已经被链接了,但是NIO API中的关键方法是新的函数“transferTo”和“transferFrom”。
其中一篇链接文章展示了如何使用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(); } } }
学习NIO可能有点棘手,所以你可能只想相信这个机制,然后再试着去学习NIO。 从个人经验来看,如果您没有经验并通过java.iostream引入IO,可能会非常难以学习。
现在使用Java 7,您可以使用以下“尝试与资源”语法:
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() ); } }
或者,更好的是,这也可以使用Java 7中引入的新的Files类来完成:
public static void copyFile( File from, File to ) throws IOException { Files.copy( from.toPath(), to.toPath() ); }
很时髦,呃?
- 这些方法是性能devise的(它们与操作系统本地I / O集成)。
- 这些方法适用于文件,目录和链接。
- 提供的每个选项都可以省略 – 它们是可选的。
实用程序类
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);
recursion复制一个目录或文件
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 );
在Java 7中很容易…
File src = new File("original.txt"); File target = new File("copy.txt"); Files.copy(src.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
请注意,所有这些机制只复制文件的内容,而不是权限等元数据。 所以,如果你要在linux上复制或移动可执行文件.sh文件,新文件将不可执行。
为了真正复制或移动文件,即获得与从命令行复制相同的结果,实际上需要使用本机工具。 一个shell脚本或者JNI。
显然,这可能会在Java 7中修复 – http://today.java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis.html 。 手指交叉!
要复制文件并将其保存到目标path,可以使用下面的方法。
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(); } }
谷歌的番石榴图书馆也有一个复制方法 :
公共静态无效复制 ( 文件来自, 文件到) 抛出IOException
- 将所有字节从一个文件复制到另一个文件。
警告:如果to
表示现有的文件,该文件将被覆盖以from
的内容。 如果from
于同一文件,则该文件的内容将被删除。
参数: from
– 源文件to
– 目标文件
抛出: IOException
– 如果发生I / O错误IllegalArgumentException
– 如果from.equals(to)
Java 7中的标准版,path.copyTo: http : //openjdk.java.net/projects/nio/javadoc/java/nio/file/Path.html http://java.sun.com/docs/books/教程/本质/ IO / copy.html
我不能相信这花了他们这么长时间来标准化一些如此常见和简单的文件复制:(
这里有三种方法可以轻松地用单行代码复制文件!
Java7 :
java.nio.file.Files#副本
private static void copyFileUsingJava7Files(File source, File dest) throws IOException { Files.copy(source.toPath(), dest.toPath()); }
Appache Commons IO :
文件实用程序#的CopyFile
private static void copyFileUsingApacheCommonsIO(File source, File dest) throws IOException { FileUtils.copyFile(source, dest); }
番石榴 :
文件复制#
private static void copyFileUsingGuava(File source,File dest) throws IOException{ Files.copy(source,dest); }
上面的代码有三个可能的问题:
- 如果getChannel引发exception,则可能会泄露一个打开的stream。
- 对于大文件,您可能会尝试一次传输更多的操作系统可以处理。
- 你忽略了transferFrom的返回值,所以它可能只是复制文件的一部分。
这就是为什么org.apache.tools.ant.util.ResourceUtils.copyResource
太复杂了。 还要注意的是,虽然transferFrom是正确的,但transferTo在Linux上的JDK 1.4上中断(参见错误号5056395 ) – Jesse Glick Jan
如果您在一个已经使用Spring的Web应用程序中,并且如果您不希望将Apache Commons IO包含在简单的文件复制中,则可以使用Spring框架的FileCopyUtils 。
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(); } } }
快速和Android的所有版本的Java也工作:
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(); }
根据我的testing,使用缓冲区的NIO副本是最快的。 从https://github.com/mhisoft/fastcopy的testing项目中查看下面的工作代码;
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(); } } }
}