【问题标题】:Write big files using RandomAccessFile class使用 RandomAccessFile 类编写大文件
【发布时间】:2019-06-25 10:45:11
【问题描述】:

我需要将大文件 (GB) 复制到另一个文件(容器)中,我想知道性能和内存使用情况。

像下面这样读取整个源文件:

RandomAccessFile f = new RandomAccessFile(origin, "r");
originalBytes = new byte[(int) f.length()];
f.readFully(originalBytes);

然后,像这样将所有内容复制到容器中:

RandomAccessFile f2 = new RandomAccessFile(dest, "wr");
f2.seek(offset);
f2.write(originalBytes, 0, (int) originalBytes.length);

一切都在内存中,对吗?那么复制大文件会影响内存并导致 OutOfMemory 异常?

按字节而不是完全读取原始文件是否更好? 在那种情况下,我应该如何进行? 提前谢谢你。

编辑:

按照mehdi maick的回答,我终于找到了解决方案: 我可以根据需要使用 RandomAccessFile 作为目标,并且因为 RandomAccessFile 有一个返回 FileChannel 的方法“getChannel”,我可以将其传递给以下方法,该方法将执行文件在我想要的目的地的位置:

     public static void copyFile(File sourceFile, FileChannel destination, int position) throws IOException {
            FileChannel source = null;
            try {
                source = new FileInputStream(sourceFile).getChannel();
                destination.position(position);
                int currentPosition=0;
                while (currentPosition < sourceFile.length())
                    currentPosition += source.transferTo(currentPosition, 32768, destination);
            } finally {
                if (source != null) {
                    source.close();
                }

            }
        }

【问题讨论】:

  • 为什么不使用字节缓冲区,并以块的形式读取原始文件?性能方面很棒。
  • 读入块/块,例如一次 64k,使用 FileInputStreamFileOutputStream
  • @AlexandarPetrov 考虑到目标文件必须用 RandomAccessFile 编写,您能否提供一个示例?谢谢。
  • @Andreas 对你也一样;)
  • 为什么目标文件必须写成RandomAccessFile?您不是简单地将现有文件连接成一个组合文件吗?

标签: java randomaccessfile


【解决方案1】:

尝试使用 async nio Channel


    public void copyFile(String src, String target) {
        final String fileName = getFileName(src);
        try (FileChannel from = (FileChannel.open(Paths.get(src), StandardOpenOption.READ));
                FileChannel to = (FileChannel.open(Paths.get(target + "/" + fileName), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE))) {
            transfer(from, to, 0l, from.size());
        }
    }

    private String getFileName(final String src) {
        File file = new File(src);
        if (file.isFile()) {
            return file.getName();
        } else {
            throw new RuntimeException("src is not a valid file");
        }
    }

    private void transfer(final FileChannel from, final FileChannel to, long position, long size) throws IOException {
        while (position < size) {
            position += from.transferTo(position, Constants.TRANSFER_MAX_SIZE, to);
        }
    }

这将创建一个读写异步通道,并有效地将数据从第一个通道传输到第二个通道。

【讨论】:

  • 容器(目标文件)包含文件的串联,我需要在容器中寻找正确的偏移量并从那里开始写入。这就是为什么我在问题中询问我们使用 RandomAccessFile 写入目标(容器)的示例...您能否使用 RandomAccessFile 为目标调整您的示例?
  • FileChannel 提供了一个position(long position) 方法来寻找所需的确切位置。
  • 我以前没见过。我会支持你,因为我现在无法测试它,如果可以,我会接受你的回答......谢谢你的时刻;)
  • 谢谢您,我接受了您的回答,并使用找到的解决方案编辑了我的问题... ;)
  • 很高兴我能帮上忙 ;)
【解决方案2】:

读入块/块,例如一次 64k,使用 FileInputStreamFileOutputStream

如果您需要提高性能,您可以尝试使用线程,一个线程用于读取,另一个线程用于写入。

您还可以使用直接 NIO 缓冲区来提高性能。
参见例如A simple rule of when I should use direct buffers with Java NIO for network I/O?

【讨论】:

  • 如果最后一个 64k 块只包含 32K 字节,会发生什么?它将写入整个 64k(最后 32k 带零)?
  • @navy1978 如果只将 32k 读入缓冲区,为什么它会写入 64k?如果是这样,你写错了代码。
  • 我没有关注你,你说要读取 64K 字节而不是我...不是 64K 的倍数)对您有意义吗?
  • @navy1978 你有一个缓冲区,例如 64k 大小,你要求从输入文件中读取字节到缓冲区中,然后你转身写入 实际 字节数读取到输出文件。为什么你会认为写入字节的代码不知道缓冲区是否已满?
  • 我在问,因为我不知道它是如何工作的,在 RandomAccessFile 中,方法“write”(其中一种方法“write”)需要一个字节数组,所以我想知道是否通过一个 64kb 数组最后一次写入全部字节或只写入部分......这是我的怀疑......
猜你喜欢
  • 1970-01-01
  • 2013-05-02
  • 2012-03-18
  • 1970-01-01
  • 1970-01-01
  • 2014-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多