在Java中,想要复制文件的唯一方式似乎总是需要打开流、声明缓冲区、读取一个文件并对其进行循环处理,最后将其写入另一个流中。网络上有很多类似的解决方案,但都有些微不同。
是否有更好的方法可以在Java语言范围内实现(也就是不涉及执行特定于操作系统的命令)?也许有可靠的开源工具包可以提供一行代码的解决方案来隐藏底层实现细节?
在Java中,想要复制文件的唯一方式似乎总是需要打开流、声明缓冲区、读取一个文件并对其进行循环处理,最后将其写入另一个流中。网络上有很多类似的解决方案,但都有些微不同。
是否有更好的方法可以在Java语言范围内实现(也就是不涉及执行特定于操作系统的命令)?也许有可靠的开源工具包可以提供一行代码的解决方案来隐藏底层实现细节?
我会避免使用像Apache Commons这样的大型API。这是一个简单的操作,并且在新的NIO包中已经内置到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.io流介绍了IO,则学习它可能是非常困难的。
如工具包中所述,Apache Commons IO 是可行的解决方案,尤其是要使用FileUtils 。copyFile()方法将为您处理所有繁重的工作。
另外,需要注意的是,最近版本的FileUtils(例如2.0.1版本)已经添加了NIO来复制文件。 NIO 可以显著提高文件复制性能, 这在很大程度上是因为NIO例程通过将直接将复制推迟到操作系统/文件系统处理,而不是通过Java层读取和写入字节来处理。如果您正在寻找性能,则值得检查是否使用最新版本的FileUtils。
现在在Java 7中,你可以使用以下带资源的try语法:
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() );
}
相当漂亮,是吧?
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 );
Paths.get("<filepath1>")
的情况下,写new java.io.File("<filepath1>").toPath()
没有任何意义。 - Holger在Java 7中,这很容易...
File src = new File("original.txt");
File target = new File("copy.txt");
Files.copy(src.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
Files.copy(Paths.get("original.txt"), Paths.get("copy.txt"), ...)
- Holger要复制文件并将其保存到目标路径,您可以使用以下方法。
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();
}
}
Google的Guava库也有一个复制方法:
public static void copy(File from, File to) throws IOException
注意: 如果to
代表一个已存在的文件,那么该文件将被覆盖为from
的内容。如果to
和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/tutorial/essential/io/copy.html
真不敢相信,竟然花费这么长时间才把文件复制这么常见和简单的功能标准化 :(
如果你正在使用Spring框架的Web应用程序,而且不想为简单的文件复制添加Apache Commons IO,那么你可以使用Spring框架的FileCopyUtils。