首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在java中自动地将多个文件从src复制到dest?

如何在java中自动地将多个文件从src复制到dest?
EN

Stack Overflow用户
提问于 2020-09-11 02:07:49
回答 5查看 881关注 0票数 3

在一个需求中,我需要将多个文件从一个位置复制到另一个网络位置。

假设我在/src位置有以下文件。

a.pdf, b.pdf, a.doc, b.doc, a.txt and b.txt

我需要立即将a.pdf, a.doc and a.txt文件atomically复制到/dest位置。

目前,我正在使用Java.nio.file.Files包和代码,如下所示

代码语言:javascript
复制
Path srcFile1 = Paths.get("/src/a.pdf");
Path destFile1 = Paths.get("/dest/a.pdf");

Path srcFile2 = Paths.get("/src/a.doc");
Path destFile2 = Paths.get("/dest/a.doc");

Path srcFile3 = Paths.get("/src/a.txt");
Path destFile3 = Paths.get("/dest/a.txt");

Files.copy(srcFile1, destFile1);
Files.copy(srcFile2, destFile2);
Files.copy(srcFile3, destFile3);

但在这个过程中,文件会被一个接一个地复制。

作为替代,为了使整个过程成为原子的,我正在考虑压缩所有的文件,并移动到/dest并解压缩到目的地。

这种方法使整个复制过程成为原子是正确的吗?任何一个人都经历过类似的概念并解决了它。

EN

回答 5

Stack Overflow用户

发布于 2020-09-24 12:55:35

这种方法使整个复制过程成为原子是正确的吗?任何一个人都经历过类似的概念并解决了它。

您可以将文件复制到新的临时目录,然后重命名该目录。

在重命名临时目录之前,您需要删除目标目录

如果目标目录中已存在您不想覆盖的其他文件,则可以将临时目录中的所有文件移动到目标目录中。

然而,这并不完全是原子的。

通过删除/dest:

代码语言:javascript
复制
String tmpPath="/tmp/in/same/partition/as/source";
File tmp=new File(tmpPath);
tmp.mkdirs();
Path srcFile1 = Paths.get("/src/a.pdf");
Path destFile1 = Paths.get(tmpPath+"/dest/a.pdf");

Path srcFile2 = Paths.get("/src/a.doc");
Path destFile2 = Paths.get(tmpPath+"/dest/a.doc");

Path srcFile3 = Paths.get("/src/a.txt");
Path destFile3 = Paths.get(tmpPath+"/dest/a.txt");

Files.copy(srcFile1, destFile1);
Files.copy(srcFile2, destFile2);
Files.copy(srcFile3, destFile3);
delete(new File("/dest"));
tmp.renameTo("/dest");
代码语言:javascript
复制
void delete(File f) throws IOException {
  if (f.isDirectory()) {
    for (File c : f.listFiles())
      delete(c);
  }
  if (!f.delete())
    throw new FileNotFoundException("Failed to delete file: " + f);
}

只需覆盖文件:

代码语言:javascript
复制
String tmpPath="/tmp/in/same/partition/as/source";
File tmp=new File(tmpPath);
tmp.mkdirs();
Path srcFile1 = Paths.get("/src/a.pdf");
Path destFile1=paths.get("/dest/a.pdf");
Path tmp1 = Paths.get(tmpPath+"/a.pdf");

Path srcFile2 = Paths.get("/src/a.doc");
Path destFile2=Paths.get("/dest/a.doc");
Path tmp2 = Paths.get(tmpPath+"/a.doc");

Path srcFile3 = Paths.get("/src/a.txt");
Path destFile3=Paths.get("/dest/a.txt");
Path destFile3 = Paths.get(tmpPath+"/a.txt");

Files.copy(srcFile1, tmp1);
Files.copy(srcFile2, tmp2);
Files.copy(srcFile3, tmp3);

//Start of non atomic section(it can be done again if necessary)

Files.deleteIfExists(destFile1);
Files.deleteIfExists(destFile2);
Files.deleteIfExists(destFile2);

Files.move(tmp1,destFile1);
Files.move(tmp2,destFile2);
Files.move(tmp3,destFile3);
//end of non-atomic section

即使第二种方法包含非原子节,复制过程本身也会使用临时目录,这样文件就不会被覆盖。

如果在移动文件的过程中进程中止,则可以轻松完成。

有关移动文件的参考信息,请参阅https://stackoverflow.com/a/4645271/10871900;有关递归删除目录的信息,请参阅https://stackoverflow.com/a/779529/10871900

票数 2
EN

Stack Overflow用户

发布于 2020-09-28 01:08:13

首先,有几种复制文件或目录的可能性。Baeldung提供了对不同可能性的非常好的见解。此外,您还可以使用Spring中的FileCopyUtils。不幸的是,所有这些方法都不是原子的。

我已经找到了一个older post,并对其进行了一点调整。您可以尝试使用低级事务管理支持。这意味着您可以在该方法之外创建一个事务,并定义在回滚中应该执行的操作。还有一篇来自Baeldung的很好的文章。

代码语言:javascript
复制
@Autowired
private PlatformTransactionManager transactionManager;

@Transactional(rollbackOn = IOException.class)
public void copy(List<File> files) throws IOException {
    TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
    TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);

    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

        @Override
        public void afterCompletion(int status) {
            if (status == STATUS_ROLLED_BACK) {
                // try to delete created files
            }
        }
    });

    try {
        // copy files
        transactionManager.commit(transactionStatus);
    } finally {
        transactionManager.rollback(transactionStatus);
    }
}

或者,您可以使用一个简单的try-catch-block。如果抛出异常,您可以删除创建的文件。

票数 2
EN

Stack Overflow用户

发布于 2020-10-01 02:30:34

你的问题缺乏原子性的目标。即使解压缩也不是原子的,VM可能会崩溃,而OutOfMemoryError正好在膨胀第二个文件的块之间。所以有一个文件完整,第二个文件不完整,第三个文件完全丢失。

我能想到的唯一一件事就是两阶段提交,就像所有建议一样,临时目标突然变成了真正的目标。这样,您就可以确定,第二个操作要么永远不会发生,要么创建最终状态。

另一种方法是之后在目标中编写一种廉价的校验和文件。这将使外部进程很容易监听此类文件的创建,并使用找到的文件验证其内容。

后者与立即提供容器/ ZIP/ archive相同,而不是在目录中堆积文件。大多数归档文件都有或支持完整性检查。

(如果目录或文件夹在写入时消失,操作系统和文件系统的行为也会有所不同。有些人接受它并将所有数据写入可恢复缓冲区。其他人仍然接受写入,但不会更改任何内容。因为设备上的目标数据块未知,所以其他数据块在第一次写入时会立即失败。)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63835385

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档