在Java中,当程序崩溃时,如何成功保存从A复制到B的文件?

huangapple go评论70阅读模式
英文:

Saving sucessfully copied Files from A to B in Java when Programm crashes?

问题

package com.mycompany;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;

public class CopyingData {
    
    public static void saveFilesAlreadyCopied(ArrayList<String> filesToSave, String fileName) {
        PrintWriter pw = null;
        try {
            pw = new PrintWriter(new FileOutputStream(fileName));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        for (String file : filesToSave) {
            pw.println(file);
        }
            
        pw.close();
    }
    
    
    public static void main(String[] args) {

        // Variables:
        File sourceDir = new File("sourceDir");
        File destinationDir = new File("destDir");
        ArrayList<String> listOfFiles = new ArrayList<>();
        ArrayList<String> filesAlreadyCopied = new ArrayList<>();
        
        // Create dirs:
        sourceDir.mkdirs();
        destinationDir.mkdirs();
        
        // Generate 1000 Files for Testing:
        for(int i = 0; i < 1000; i++) {
            File textFile = new File("sourceDir/textfile" + i + ".txt");
            try {
                textFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
            listOfFiles.add(textFile.getAbsolutePath());
        }
        
        // Copy files from sourceDir to destinationDir:
        for(String file : listOfFiles) {
            Path sourcePath = Paths.get(file);
            Path destPath = Paths.get(destinationDir.getAbsolutePath() + File.separator + new File(file).getName());
            try {
                Files.copy(sourcePath, destPath, StandardCopyOption.REPLACE_EXISTING);
                filesAlreadyCopied.add(file);
                saveFilesAlreadyCopied(filesAlreadyCopied, sourceDir.getAbsolutePath() + File.separator + "backupCopiedFiles.txt");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }				
        
    }

}
英文:

i have to copy lots of files from A to B in Java, problem is that the Programm might crash.
Already sucessfully copied Files cannot be copied again, when the programm restarts. How can I save in a textFile already copied Files?

My solution below is not 100% sucessfull, sometimes it doesnt save all the successfully copied files.
I wonder if theres a better approach to this issue.Thanks a lot in advance.

 package com.mycompany;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
public class CopyingData {
public static void saveFilesAlreadyCopied(ArrayList&lt;String&gt; filesToSave, String fileName) {
PrintWriter pw = null;
try {
pw = new PrintWriter(new FileOutputStream(fileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
for (String file : filesToSave) {
pw.println(file);
}
pw.close();
}
public static void main(String[] args) {
//Variables:
File sourceDir = new File(&quot;sourceDir&quot;);
File destinationDir = new File(&quot;destDir&quot;);
ArrayList&lt;String&gt; listOfFiles = new ArrayList&lt;&gt;();
ArrayList&lt;String&gt; filesAlreadyCopied = new ArrayList&lt;&gt;();
//Create dirs:
sourceDir.mkdirs();
destinationDir.mkdirs();
//Generate 1000 Files for Testing:
for(int i = 0;i&lt;1000;i++) {
File textFile = new File(&quot;sourceDir/textfile&quot;+i+&quot;.txt&quot;);
try {
textFile.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
listOfFiles.add(textFile.getAbsolutePath());
}
//Copy files from sourceDir to destinationDir:
for(String file : listOfFiles) {
Path sourcePath = Paths.get(file);
Path destPath = Paths.get(destinationDir.getAbsolutePath() + File.separator + new File(file).getName());
try {
Files.copy(sourcePath, destPath, StandardCopyOption.REPLACE_EXISTING);
filesAlreadyCopied.add(file);
saveFilesAlreadyCopied(filesAlreadyCopied,sourceDir.getAbsolutePath() + File.separator +&quot;backupCopiedFiles.txt&quot;);
} catch (IOException e) {
e.printStackTrace();
}
}				
}
}

答案1

得分: 1

一个可能的问题是您可能会耗尽磁盘空间,我能理解在复制了数百万个文件、数太字节的数据之后,在最后一个文件崩溃时,您希望释放一些磁盘空间并继续处理最后一个文件。

但请考虑以下情况:所编写的算法基本如下(假设检查点文件 "chkpt" 包含成功复制的文件列表):

  1. 对于每个文件 f
  2. 复制文件 f
  3. 更新检查点文件 chkpt

因此,如果步骤 #2 未能写入文件系统,那么您可以根据 chkpt 的内容恢复。但如果步骤 #3 未能写入文件系统会发生什么呢?(您会为检查点文件创建另一个检查点文件吗?)这种情况发生的几率会随着 chkpt 的大小增加而增加,而您当前将其设置为包含要复制的所有文件的完整列表(对于大量文件的情况可能会成为问题)。

我可能会推荐以下方法(供您思考),以减少无法恢复的可能性:

  1. 如果您想要保留要复制的文件列表,那么首先在磁盘上创建一个文件,只创建一次;例如,filelist.txt,其中包含 file1.txtfile2.txt 等。
  2. 在开始复制之前,创建一个名为 copying 的文件,其中包含“下一个要复制的文件”,例如,如果您已经复制了文件 file1.txtfile31.txt,那么它将包含 file32.txt
  3. 开始复制文件(例如 file32.txt)。
  4. 如果复制失败并且程序崩溃,文件 copying 的存在表明了停止的位置 —— 重新启动复制 file32.txt
  5. 如果复制成功,请将 copying 做一个“移动”操作(文件重命名),改为 completed(因此,文件 completed 将包含文件名 file32.txt……当然,文件名可以随意设置)。文件的移动/重命名通常不会失败,也不应像写入大文件那样经常出现损坏。
  6. 继续处理下一个文件:使用列表中的下一个文件名创建一个新文件 copying(不要修改文件列表)。

这样做有意义吗?(您有何想法?)与您目前拥有的内容相比,以上工作量不会增加太多,但可能会降低遇到“无法恢复”的情况的几率(即损坏的检查点文件)。

英文:

One possible problem you might have is running out of disk space, and I can see how after copying millions of files, terabytes of data, & then crashing on the last file, you would want to just free up some disk space & continue with the last file.

But consider this: the algorithm as written is basically as follows (assuming a checkpoint file "chkpt" contains list of files successfully copied):

  1. for each file f,
  2. copy file f,
  3. update checkpoint file chkpt

So, if step #2 fails to write to the file system, then you can resume based on the contents of chkpt. But what happens if step #3 fails to write to the file system? (Do you create a checkpoint file for the checkpoint file?) The odds of this happening increase with the size of chkpt, which you currently have as containing the entire list of files to copy (could be a problem for a large list of files).

I might recommend the following (as food for thought) to decrease the odds of being unable to recover:

  1. if you want to persist a list of files to copy, then create that first on disk, just once; e.g., filelist.txt, containing file1.txt, file2.txt, etc
  2. before starting the copy, create a file called e.g. copying containing the "next file to copy", e.g., if you've already copied files file1.txt through file31.txt, it would contain file32.txt
  3. start to copy the file (eg file32.txt)
  4. if the copy fails, and the program crashes, the existence of a file called copying indicates where things stopped -- restart copy of file32.txt
  5. if the copy succeeded, do a "move" (file rename) of copying to completed (so, file completed would contain filename file32.txt ...also, obviously, call these files whatever you want). A file move/rename won't usually fail, nor should it become corrupted as often as writing a large file.
  6. continue with the next file: create a new file copying with the next filename in the list (don't modify the file list)

Does that make sense? (Thoughts?) The level of effort above what you currently have isn't much more, but it might reduce the chance of running into an "unrecoverable" state (i.e., corrupted checkpoint file)..

答案2

得分: 0

我已对您的代码进行了一些更改,以处理失败情况,请尝试以下代码,让您的老板满意:

import java.io.*;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.stream.Collectors;

public class CopyingData {

    public static void saveFilesAlreadyCopied(ArrayList<String> filesToSave, String fileName) {
        PrintWriter pw = null;
        try {
            pw = new PrintWriter(new FileOutputStream(fileName));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        for (String file : filesToSave) {
            pw.println(file);
        }

        pw.close();
    }

    public static void main(String[] args) throws IOException {
        String operationFolder = "C:/Downloads/code_play-folder/";
        // 变量:
        File sourceDir = new File(operationFolder + "sourceDir");
        File destinationDir = new File(operationFolder + "destDir");
        String progressFilePath = operationFolder + "sourceDir" + File.separator + "backupCopiedFiles.txt";
        File progressReportFile = new File(progressFilePath);
        ArrayList<String> listOfFiles = new ArrayList<>();
        ArrayList<String> filesAlreadyCopied = new ArrayList<>();
        if (progressReportFile.exists()) {
            filesAlreadyCopied.addAll(Files.readAllLines(Paths.get(progressFilePath)).stream().filter(data -> !data.trim().equals("")).collect(Collectors.toList()));
        }

        // 创建目录:
        sourceDir.mkdirs();
        destinationDir.mkdirs();

        // 生成1000个用于测试的文件:
        for (int i = 0; i < 1000; i++) {
            String fileName = operationFolder + "sourceDir/textfile" + i + ".txt";
            File textFile = new File(fileName);
            try {
                if (!textFile.exists()) {
                    textFile.createNewFile();
                    Files.write(
                            Paths.get(fileName),
                            ("contentToAppend" + fileName).getBytes(),
                            StandardOpenOption.APPEND);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            listOfFiles.add(textFile.getAbsolutePath());
        }

        listOfFiles.removeAll(filesAlreadyCopied);
        // 从sourceDir复制文件到destinationDir:
        for (String file : listOfFiles) {
            Path sourcePath = Paths.get(file);
            Path destPath = Paths.get(destinationDir.getAbsolutePath() + File.separator + new File(file).getName());
            try {
                Files.copy(sourcePath, destPath, StandardCopyOption.REPLACE_EXISTING);
                filesAlreadyCopied.add(file);
                saveFilesAlreadyCopied(filesAlreadyCopied, sourceDir.getAbsolutePath() + File.separator + "backupCopiedFiles.txt");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
英文:

I have made some changes to your code, to handle fail scenarios, please try below code and make happy your boss:

import java.io.*;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.stream.Collectors;
public class CopyingData {
public static void saveFilesAlreadyCopied(ArrayList&lt;String&gt; filesToSave, String fileName) {
PrintWriter pw = null;
try {
pw = new PrintWriter(new FileOutputStream(fileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
for (String file : filesToSave) {
pw.println(file);
}
pw.close();
}
public static void main(String[] args) throws IOException {
String operationFolder = &quot;C:/Downloads/code_play-folder/&quot;;
//Variables:
File sourceDir = new File(operationFolder+&quot;sourceDir&quot;);
File destinationDir = new File(operationFolder+&quot;destDir&quot;);
String progressFilePath = operationFolder+&quot;sourceDir&quot; +File.separator + &quot;backupCopiedFiles.txt&quot;;
File progressReportFile = new File(progressFilePath);
ArrayList&lt;String&gt; listOfFiles = new ArrayList&lt;&gt;();
ArrayList&lt;String&gt; filesAlreadyCopied = new ArrayList&lt;&gt;();
if (progressReportFile.exists()){
filesAlreadyCopied.addAll(Files.readAllLines(Paths.get(progressFilePath)).stream().filter(data-&gt; !data.trim().equals(&quot;&quot;)).collect(Collectors.toList()));
}
//Create dirs:
sourceDir.mkdirs();
destinationDir.mkdirs();
//Generate 1000 Files for Testing:
for(int i = 0;i&lt;1000;i++) {
String fileName = operationFolder+&quot;sourceDir/textfile&quot; + i + &quot;.txt&quot;;
File textFile = new File(fileName);
try {
if (!textFile.exists()) {
textFile.createNewFile();
Files.write(
Paths.get(fileName),
(&quot;contentToAppend&quot;+fileName).getBytes(),
StandardOpenOption.APPEND);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
listOfFiles.add(textFile.getAbsolutePath());
}
listOfFiles.removeAll(filesAlreadyCopied);
//Copy files from sourceDir to destinationDir:
for(String file : listOfFiles) {
Path sourcePath = Paths.get(file);
Path destPath = Paths.get(destinationDir.getAbsolutePath() + File.separator + new File(file).getName());
try {
Files.copy(sourcePath, destPath, StandardCopyOption.REPLACE_EXISTING);
filesAlreadyCopied.add(file);
saveFilesAlreadyCopied(filesAlreadyCopied,sourceDir.getAbsolutePath() + File.separator +&quot;backupCopiedFiles.txt&quot;);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

huangapple
  • 本文由 发表于 2020年8月29日 02:03:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/63638864.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定