【问题标题】:Best practice to handle IO Exceptions while coping directory(s) and file(s)?处理目录和文件时处理 IO 异常的最佳实践?
【发布时间】:2020-05-24 00:11:30
【问题描述】:

示例代码如下。它将目标文件和目录从一个位置复制到另一个位置。在跨网络处理文件时,处理 IO 异常的最佳实践是什么?

我使用了 printStackTrace(),但感觉这只是一个更好的解决方案的占位符。正在记录答案吗?除了记录之外,是否应该有另一个步骤来实际“处理”错误?

感谢您的反馈。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
   This is a test program to copy a directory(s) & file(s) from one location to another.
*/

public class CopyTest{

   public static void main(String[] args) {

      //Declarations 
      String sourcePath = "I:\\MB\\PO"; 
      String destPath = "C:\\testPO\\";  
      System.out.println("Source path:  " + sourcePath);  
      System.out.println("Destination path:  " + destPath);  
      File source = new File(sourcePath);  
      File dest = new File(destPath);  

      //Process
      //Call to method copyUsingStream
      long start = System.nanoTime(); //start recording how much time the copy takes.
      copyUsingStream(source, dest);  //method to copy the directory/files.
      System.out.println("Time taken to copy the file: "+(System.nanoTime() -start) + " nanoseconds");

   } //end main method

   /**
      The copyUsingStream method is a recursive method to copy folders and files from one location to another.
   */

   private static void copyUsingStream(File source, File dest) {   

      if (!source.isDirectory()){ 
         // If source is a file -> copy it to the new folder
         InputStream inStream = null;
         OutputStream outStream = null;
         try {
            inStream = new FileInputStream(source);
            outStream = new FileOutputStream(dest);
            byte[] buffer = new byte[1024];
            int length;

            while ((length = inStream.read(buffer)) > 0) {
               outStream.write(buffer, 0, length);
            }
         } catch(IOException ioe) {
            ioe.printStackTrace();
         } finally {
            try{
               inStream.close();
               outStream.close();
               System.out.println("File copied from " + source + " to " + dest + "successfully");
            } catch(IOException ioe2) {
               ioe2.printStackTrace();
            }
         } 
      } else {

         //If a directory -> create the directory inside the new destination
         //List all contents

         if (!dest.exists()) {
            dest.mkdir();
            System.out.println("Directory copied from " + source + " to " + dest + "successfully");
         }

         String folder_contents[] = source.list();

         for (String file : folder_contents) {

            File srcFile = new File(source, file);
            File destFile = new File(dest, file);

            copyUsingStream(srcFile, destFile);

         }

      }

   } //end method copyUsingStream

} //end class CopyTest

没有catch的方法:

   private static void copyUsingStream(File source, File dest) throws IOException {   

      if (!source.isDirectory()){ 
         // If source is a file -> copy it to the new folder
         InputStream inStream = null;
         OutputStream outStream = null;
         try {
            inStream = new FileInputStream(source);
            outStream = new FileOutputStream(dest);
            byte[] buffer = new byte[1024];
            int length;

            while ((length = inStream.read(buffer)) > 0) {
               outStream.write(buffer, 0, length);
            }
         } finally {
               inStream.close();
               outStream.close();
               System.out.println("File copied from " + source + " to " + dest + "successfully");
         } 
      } else {

         //If a directory -> create the directory inside the new destination
         //List all contents

         if (!dest.exists()) {
            dest.mkdir();
            System.out.println("Directory copied from " + source + " to " + dest + "successfully");
         }

         String folder_contents[] = source.list();

         for (String file : folder_contents) {

            File srcFile = new File(source, file);
            File destFile = new File(dest, file);

            copyUsingStream(srcFile, destFile);

         }

      }

   } //end method copyUsingStream

【问题讨论】:

  • 您可以使用java.nioFiles.copy,而不是使用流来复制内容,它需要两个Path 对象。您可以使用 Filesystems.getDefault().getPath("DirectoryNameHere", "FileNameHere"); 实例化 Path 对象
  • 您好 Powerlord,感谢您的反馈。我应该澄清一下,我使用蒸汽只是为了使用它的学术目的。我确实在我的研究中看到 nio 的 file.copy 是完成此操作的更清洁和“更好”的方式。

标签: java file inputstream outputstream ioexception


【解决方案1】:

这在很大程度上取决于您的应用程序。

无论如何都继续运行的应用程序(例如 Web 服务器、守护程序和批处理器)通常会在文件中记录此类错误以及时间戳、线程 ID 和可能的其他有用信息。

我在组合两个日志文件方面有很好的经验。

  1. myapp.log 只接收重要消息,通常是警告和错误。此文件适用于普通用户和系统操作员。
  2. debug.log 是给开发者的。它提供错误发生前的调试消息,但只要一切正常,就没有消息。要启用此功能,需要一个内存缓冲区。

如果您对该缓冲区感兴趣,可以查看http://stefanfrings.de/bfUtilities/index.html。该网站是德文的,但图书馆和它的文档是英文的。

在桌面 GUI 应用程序中,当错误中止请求的操作时,最好在弹出窗口中显示简短的错误消息,并在可扩展框中隐藏详细信息(堆栈跟踪)。不要忘记清楚地告诉用户什么操作失败了。对于您的开发人员来说,异常本身可能已经足够清楚了,但普通用户期望的技术文本较少。例如:“从服务 weather.com 加载天气信息失败:连接失败”,后跟堆栈跟踪。

对于立即停止的控制台应用程序,我更喜欢直接在屏幕上查看由printStackTrace() 编写的堆栈跟踪。

【讨论】:

  • 谢谢斯特凡。这最终将成为创建独立 GUI 实用程序的测试平台。
【解决方案2】:

正如 Stefan 所说,这取决于应用程序。

一个好的经验法则是:不要捕获异常,除非您准备好采取特定操作,而不是打印或记录它,或者没有可以传播它的调用者。

如果您有复制文件的通用方法,则该方法不应假设调用它的原因。它的工作是复制文件。只有当它成功完成该任务时它才应该返回。如果不成功,它应该抛出异常而不是返回。

因此,对于一般的复制方法,您需要将throws IOException 添加到方法签名中,并在方法本身中包含零个 try/catch 块。这让调用者决定如何处理失败。 GUI 应用程序可能会显示错误对话框。服务可能只是记录异常并稍后重试。

您自己应该只在可能的最高级别捕获和记录异常。 GUI 应用程序会在显示错误对话框之前将其记录下来。 (您可能还希望在对话框中包含堆栈跟踪的文本,在可展开的“显示详细信息”部分中。)服务可能有一个主循环或主执行方法,其中没有更高的调用者可以接收异常被传播,所以除了记录之外别无他法。

【讨论】:

  • 感谢斯特凡。我重新设计了方法以取出捕获并将 throws IOException 添加到方法头中。是否应该对此方法本身进行任何其他与稍后处理异常相关的更改?
  • 并非如此。 Javadoc 总是很好。但就功能而言,添加throws 子句意味着错误处理取决于调用者,而不是该方法的责任,因此无需再做任何事情。
猜你喜欢
  • 2018-01-03
  • 1970-01-01
  • 1970-01-01
  • 2017-05-11
  • 2013-05-09
  • 2011-11-10
  • 1970-01-01
  • 2013-04-22
相关资源
最近更新 更多