【问题标题】:Does not closing a FileOutPutStream not write anything to the file?不关闭 FileOutPutStream 不会向文件写入任何内容?
【发布时间】:2019-04-25 22:25:54
【问题描述】:

我有一个将给定输入流写入给定输出流的函数。代码如下。

static void copyStream(InputStream is, OutputStream os) throws IOException {
    byte[] buffer = new byte[4096];
    int len;
    try {
        while ((len = is.read(buffer)) != -1) {
            os.write(buffer, 0, len);
        }
    }
}    

上面的函数就是从这个函数调用的

public static void copyFile(File srcFile, File destFile) throws IOException {
    FileInputStream fis = new FileInputStream(srcFile);
    try {
        FileOutputStream fos = new FileOutputStream(destFile);
        try {
            **copyStream**(fis, fos);
        } finally {
            if (fos != null)
                fos.close();
        }
    } finally {
        if (fis != null)
            fis.close();
    }
}

在这个函数中,我一次写入 4 MB。我使用此功能复制图像。有时我会看到未创建目标文件,因此在尝试读取该文件以供将来处理时发生异常。我猜罪魁祸首是没有关闭资源。我的假设好吗?我的功能可能失败的原因是什么?请帮忙

【问题讨论】:

  • 文件是在调用此方法之前创建的,所以这里的异常不会阻止文件的创建。
  • 有问题或没有问题,你还是应该关闭你的资源。
  • 同意。我确实关闭了它们。但是我仍然无法弄清楚为什么访问复制的文件偶尔会引发异常(这个问题非常零星)
  • 能否也提供调用者方法?
  • 用调用函数更新了问题

标签: java file fileoutputstream


【解决方案1】:

我相信,鉴于 InputStreamOutputStream 安装正确。 在末尾添加os.flush();。当然,这两个流也应该在调用者中关闭。

作为替代方案,您可以使用 Apache IO 工具 org.apache.commons.io.IOUtils.copy(InputStream input, OutputStream output)

【讨论】:

  • flush作为close的一部分自动完成。除非您想在不关闭的情况下刷新(还),否则无需显式执行此操作。
  • 是的,但是我没有看到调用者方法,因此如果以后没有正确关闭资源,flush 应该会有所帮助。
【解决方案2】:

是的,您绝对必须关闭目标文件,以确保从 JVM 到操作系统的所有缓存都已刷新,并且文件已准备好供读者使用。

以您的方式复制大文件在代码中简洁但在操作上效率低下。考虑升级您的代码以使用更高效的 NIO 方法,documented here 在博客文章中。如果该博客消失,代码如下:

实用类:

public final class ChannelTools {
  public static void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException {
    final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
    while (src.read(buffer) != -1) {
      // prepare the buffer to be drained
      buffer.flip();
      // write to the channel, may block
      dest.write(buffer);
      // If partial transfer, shift remainder down
      // If buffer is empty, same as doing clear()
      buffer.compact();
    }
    // EOF will leave buffer in fill state
    buffer.flip();
    // make sure the buffer is fully drained.
    while (buffer.hasRemaining()) {
      dest.write(buffer);
    }
  }
}

InputStreamOutputStream 的使用示例:

// allocate the stream ... only for example
final InputStream input = new FileInputStream(inputFile);
final OutputStream output = new FileOutputStream(outputFile);
// get an channel from the stream
final ReadableByteChannel inputChannel = Channels.newChannel(input);
final WriteableByteChannel outputChannel = Channels.newChannel(output);
// copy the channels
ChannelTools.fastChannelCopy(inputChannel, outputChannel);
// closing the channels
inputChannel.close();
outputChannel.close()

Wikipedia 中还记录了一种更简洁的方法,它可以用更少的代码实现相同的目标:

// Getting file channels
FileChannel in = new FileInputStream(source).getChannel();
FileChannel out = new FileOutputStream(target).getChannel();

// JavaVM does its best to do this as native I/O operations.
in.transferTo(0, in.size(), out);

// Closing file channels will close corresponding stream objects as well.
out.close();
in.close();

【讨论】:

  • 感谢您的回答。我了解翻转和紧凑的重要性。但是为什么副本会如此零星地失败呢?我的意思是这个功能可能会失败 10000 次。它可能失败的原因是什么?
  • 你的读者和作者同步了吗?读者如何知道作者何时完全完成?如果它正在监视目录中出现的文件,那么您就有潜在的竞争条件。
  • 不,它不在同一个流程中,我们将文件写入新位置并尝试在代码中的某处读取它。我想你是对的,这是一个竞争条件。感谢您的宝贵时间
猜你喜欢
  • 1970-01-01
  • 2021-01-21
  • 2014-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-19
  • 1970-01-01
相关资源
最近更新 更多