Java 8:递归复制目录?
Posted
技术标签:
【中文标题】Java 8:递归复制目录?【英文标题】:Java 8: Copy directory recursively? 【发布时间】:2015-05-18 13:04:07 【问题描述】:我看到 Java 8 已经显着清理了将文件内容读入字符串:
String contents = new String(Files.readAllBytes(Paths.get(new URI(someUrl))));
我想知道是否有类似的东西(更简洁/更少代码/更简洁)用于递归复制目录。在 Java 7 领域,它仍然是这样的:
public void copyFolder(File src, File dest) throws IOException
if(src.isDirectory())
if(!dest.exists())
dest.mkdir();
String files[] = src.list();
for (String file : files)
File srcFile = new File(src, file);
File destFile = new File(dest, file);
copyFolder(srcFile,destFile);
else
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0)
out.write(buffer, 0, length);
in.close();
out.close();
Java 8 有什么改进吗?
【问题讨论】:
仅供参考Files.readAllBytes(Paths.get(new URI(someUrl))
自 Java 7 起可用。
else block 可以用java.nio.file.Files#copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...)
完成,也可以从 1.7 开始
查看FileVisitor的javadoc中的例子:docs.oracle.com/javase/7/docs/api/java/nio/file/…
这个org.apache.commons.io.FileUtils.copyDirectory(File, File) 怎么样......它可以确保工作安全......:D
【参考方案1】:
这样代码看起来简单一些
import static java.nio.file.StandardCopyOption.*;
public void copyFolder(Path src, Path dest) throws IOException
try (Stream<Path> stream = Files.walk(src))
stream.forEach(source -> copy(source, dest.resolve(src.relativize(source))));
private void copy(Path source, Path dest)
try
Files.copy(source, dest, REPLACE_EXISTING);
catch (Exception e)
throw new RuntimeException(e.getMessage(), e);
【讨论】:
它确实会复制子文件夹及其内容。 –1:流永远不会关闭。 ***.com/a/60621544 更好。 你在说什么流?该解决方案不使用流。 docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/… 我删除了 -1 (但我仍然更喜欢this 答案,原因在那里说明)。【参考方案2】:使用Files.walkFileTree
:
Files.walk
时)
优雅地处理IOException
。
(当添加适当的异常处理而不是简单的printStackTrace
时,这里的一些其他答案会变得更加困难)
public void copyFolder(Path source, Path target, CopyOption... options)
throws IOException
Files.walkFileTree(source, new SimpleFileVisitor<Path>()
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException
Files.createDirectories(target.resolve(source.relativize(dir)));
return FileVisitResult.CONTINUE;
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException
Files.copy(file, target.resolve(source.relativize(file)), options);
return FileVisitResult.CONTINUE;
);
这是做什么的
递归遍历目录中的所有文件。 遇到目录时 (preVisitDirectory
):
在目标目录下创建对应的。
遇到常规文件时 (visitFile
):
复制它。
options
可用于根据您的需要定制副本。例如要覆盖目标目录中的现有文件,请使用copyFolder(source, target, StandardCopyOption.REPLACE_EXISTING);
【讨论】:
由于上述原因,比许多其他答案要好得多。应该提到的一个缺点:当安全管理器阻止访问某些文件时,无法访问的文件的副本将静默失败。 (但这应该很少见,通常不使用安全管理器。) 如果没有符号链接,这个解决方案是可以的,否则,它将复制它们的目标目录并将链接变成一个带有链接名称的空目录。 如果要关注符号链接,可以将Files.walkFileTree(source, visitor)
替换为Files.walkFileTree(source, EnumSet.of(FOLLOW_LINKS), Integer.MAX_VALUE, visitor)
【参考方案3】:
下面的代码怎么样
public void copyFolder(File src, File dest) throws IOException
try (Stream<Path> stream = Files.walk(src.toPath()))
stream.forEachOrdered(sourcePath ->
try
Files.copy(
/*Source Path*/
sourcePath,
/*Destination Path */
src.toPath().resolve(dest.toPath().relativize(sourcePath)));
catch (IOException e)
throw new UncheckedIOException(e);
);
【讨论】:
使用 Path API 可以更优雅地确定目标路径:Path target = targetDir.resolve(sourceDir.relativize(path))
您好 Carl,在复制文件时顺序有什么影响?我想 forEach 应该已经足够了。
请不要捕获异常和 printStackTrace。将 IOException 包装成 UncheckedIOException 是合适的。
@OlivierCailloux 是的,这更贴切。【参考方案4】:
此版本使用 Java 8 建议的 Files.walk 和 Path 参数。
public static void copyFolder(Path src, Path dest)
try
Files.walk( src ).forEach( s ->
try
Path d = dest.resolve( src.relativize(s) );
if( Files.isDirectory( s ) )
if( !Files.exists( d ) )
Files.createDirectory( d );
return;
Files.copy( s, d );// use flag to override existing
catch( Exception e )
e.printStackTrace();
);
catch( Exception ex )
ex.printStackTrace();
【讨论】:
【参考方案5】:还有一个版本:
static void copyFolder(File src, File dest)
// checks
if(src==null || dest==null)
return;
if(!src.isDirectory())
return;
if(dest.exists())
if(!dest.isDirectory())
//System.out.println("destination not a folder " + dest);
return;
else
dest.mkdir();
if(src.listFiles()==null || src.listFiles().length==0)
return;
String strAbsPathSrc = src.getAbsolutePath();
String strAbsPathDest = dest.getAbsolutePath();
try
Files.walkFileTree(src.toPath(), new SimpleFileVisitor<Path>()
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException
File dstFile = new File(strAbsPathDest + file.toAbsolutePath().toString().substring(strAbsPathSrc.length()));
if(dstFile.exists())
return FileVisitResult.CONTINUE;
if(!dstFile.getParentFile().exists())
dstFile.getParentFile().mkdirs();
//System.out.println(file + " " + dstFile.getAbsolutePath());
Files.copy(file, dstFile.toPath());
return FileVisitResult.CONTINUE;
);
catch (IOException e)
//e.printStackTrace();
return;
return;
其代码使用 java8 Files.walkFileTree 函数。
【讨论】:
【参考方案6】:我的版本:
static private void copyFolder(File src, File dest)
// checks
if(src==null || dest==null)
return;
if(!src.isDirectory())
return;
if(dest.exists())
if(!dest.isDirectory())
//System.out.println("destination not a folder " + dest);
return;
else
dest.mkdir();
File[] files = src.listFiles();
if(files==null || files.length==0)
return;
for(File file: files)
File fileDest = new File(dest, file.getName());
//System.out.println(fileDest.getAbsolutePath());
if(file.isDirectory())
copyFolder(file, fileDest);
else
if(fileDest.exists())
continue;
try
Files.copy(file.toPath(), fileDest.toPath());
catch (IOException e)
//e.printStackTrace();
【讨论】:
请考虑添加一些关于代码的解释。 我添加了一些昂贵的支票。如果没有检查外部代码,它很有用,例如 - 来自用户的原始数据。 @pwipo 我只是想感谢您提供此代码,我发现的大多数示例都不起作用,但这完美无缺,您通过自动化为我节省了很多时间,谢谢! ! 真的吗?你要在同一个对象上多次调用 listFiles() 吗??? 如果我弄错了@blake-mcbride,请纠正我,但我看不到他是如何在同一个对象上调用 listFiles() 的?它在 for 循环之外,仅在较低目录中再次调用以进行递归...【参考方案7】:可用于将源(文件或目录)复制到目标(目录)
void copy(Path source, Path target, boolean override) throws IOException
Path target = target.resolve(source.toFile().getName());
Files.walkFileTree(source, new FileVisitor<Path>()
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
Path targetDir = target.resolve(source.relativize(dir));
if(Files.notExists(targetDir))
Files.createDirectory(targetDir);
return FileVisitResult.CONTINUE;
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
Files.copy(file, target.resolve(source.relativize(file))));
return FileVisitResult.CONTINUE;
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException
throw new RuntimeException("Copying file " + file + " failed", exc);
// Consider looking at FileVisitResult options...
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException
if (exc != null)
// TODO...
return FileVisitResult.CONTINUE; // Or whatever works for you
);
【讨论】:
以上是关于Java 8:递归复制目录?的主要内容,如果未能解决你的问题,请参考以下文章