题
它一直困扰我的唯一方法复制一文件在Java涉及开口流,宣布一个缓冲区,在阅读一个文件,循环,通过它,和它写出来的其他蒸汽。该网络充斥着类似,但仍然稍稍不同的实现这种类型的解决方案。
有没有更好的方法停留的范围内Java语言(意味着没有涉及exec-OS ing具体指令)?也许在一些可靠的公开来源工具包,这将至少掩盖这一根本执行情况,并提供一线解决方案吗?
解决方案
正如上面提到的工具包,Apache Commons IO是要走的路,特别是 FileUtils 。 copyFile();它为你处理所有繁重的工作。
作为附言,请注意最新版本的FileUtils(例如2.0.1版本)已添加使用NIO复制文件; NIO可以显着提高文件复制性能,这在很大程度上是因为NIO例程将复制直接推迟到OS /文件系统,而不是通过Java层读取和写入字节来处理它。因此,如果您正在寻找性能,可能需要检查您是否使用了最新版本的FileUtils。
其他提示
我会避免像apache公共话那样使用mega 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,那么学习它将是一件非常困难的事情。
现在使用Java 7,您可以使用以下try-with-resource语法:
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() );
}
漂亮时髦,嗯?
- 这些方法性设计的(他们融入操作系统本地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);
复制一个目录或文件的递归
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);
要复制文件并将其保存到目标路径,您可以使用以下方法。
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();
}
}
请注意,所有这些机制仅复制文件的内容,而不复制权限等元数据。因此,如果您要在Linux上复制或移动可执行文件.sh文件,则新文件将无法执行。
为了真正复制或移动文件,即获得与从命令行复制相同的结果,您实际上需要使用本机工具。 shell脚本或JNI。
显然,这可以在java 7中修复 - http://today.java.net/pub/a/today/2008/07/03/jsr-203-new-file-apis.html 。手指交叉!
谷歌的番石榴库还有一个 复制的方法:
public static void 复制(文件 from, 文件 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
我无法相信它们花了这么长时间来标准化文件复制这么常见和简单的事情:(
上述代码存在三个可能的问题:
- 如果getChannel抛出异常,您可能会泄漏打开的流。
- 对于大型文件,您可能尝试一次性传输比操作系统可以处理的更多文件。
- 您忽略了transferFrom的返回值,因此它可能只是复制文件的一部分。 醇>
这就是 org.apache.tools.ant.util.ResourceUtils.copyResource
如此复杂的原因。另请注意,虽然transferFrom没问题,但是在Linux上JDT 1.4上的transferTo会中断(请参阅错误ID:5056395 )&#8211; Jesse Glick Jan
如果您在已经使用Spring的Web应用程序中,并且如果您不想包含Apache Commons IO以进行简单的文件复制,则可以使用 FileCopyUtils 。
以下三种方法可以轻松地使用单行代码复制文件!
<强> 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);
}
番石榴:
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();
}
}
}
根据我的测试,使用缓冲区的NIO拷贝是最快的。请参阅我的测试项目中的以下工作代码: 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();
}
}
}
}
快速使用Java的所有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();
}
派对有点晚了,但这里是使用各种文件复制方法复制文件所用时间的比较。我通过这些方法循环了10次并取平均值。使用IO流的文件传输似乎是最糟糕的候选者:
以下是方法:
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);
}
使用NIO通道类时我能看到的唯一缺点是我仍然无法找到显示中间文件复制进度的方法。