Java:使用通道高效复制文件

问题描述 投票:0回答:2

我读了一篇关于转让副本的文章 位于 https://www.ibm.com/developerworks/library/j-zerocopy/。建议用户通道进行IO操作。

有一个复制文件操作的基准,位于 https://baptiste-wicht.com/posts/2010/08/file-copy-in-java-benchmark.html

根据基准测试,我可以使用 nio buffernio trasfer

我还阅读了FileChannel 在操作系统级别进行缓冲 这里如何在Java中实现缓冲/批处理的FileChannel?

使用缓冲区或不使用缓冲区复制文件哪种更有效。

nio 缓冲区代码

public static void nioBufferCopy(File sourceFile, File targetFile, int BUFFER) {
        FileChannel inputChannel = null;
        FileChannel outputChannel = null;
        try {
            inputChannel = new FileInputStream(sourceFile).getChannel();
            outputChannel = new FileOutputStream(targetFile).getChannel();
            ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER);
            while (inputChannel.read(buffer) != -1) {
                buffer.flip();          
                while(buffer.hasRemaining()){
                    outputChannel.write(buffer);
                }           
                buffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
           //close resource
        }
}

nio 转账代码

public void copyFileWithChannels(File aSourceFile, File aTargetFile) {              
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        FileInputStream inStream = null;
        FileOutputStream outStream = null;      
        try {
            inStream = new FileInputStream(aSourceFile);
            inChannel = inStream.getChannel();
            outStream = new  FileOutputStream(aTargetFile);        
            outChannel = outStream.getChannel();
            long bytesTransferred = 0;
            while(bytesTransferred < inChannel.size()){
                bytesTransferred += inChannel.transferTo(bytesTransferred, inChannel.size(), outChannel);
            }
        }       
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            //close resource
        }              
}
java io nio file-copying
2个回答
3
投票

这个问题之前被问过:

Java NIO FileChannel 与 FileOutputstream 性能/实用性

TL.DR.:您的 JVM 在什么上运行很重要,但大多数情况下

java.nio
稍微快一些。


0
投票

我们有一个用例,需要复制 GB 的文件,我们的经验 NIO 优于传统 IO,但使用具有适当缓冲区大小的通道概念。通道提供跨多个缓冲区执行单个 I/O 操作的能力。我们使用了 FileChannel 的想法,与缓冲区相比,它是线程安全的,并且还支持当前文件位置的概念,有助于确定下一个数据项将被读取或写入的位置。 我们使用 ReadableByteChannel 和 WritableByteChannel 的实现是

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

import org.apache.commons.io.IOUtils;

/**
 * A file channel that leverages NIO classes internally to perform read/write operations on the provided streams to achieve high-performance I/O.
 *
 */
public final class FileChannel implements Closeable {

    // default buffer size used for reading and writing
    private static final int BUFFER_SIZE = 65536;

    private final ReadableByteChannel source;
    private final WritableByteChannel destination;
    private final int bufferSize;
    private final boolean shouldClose;

    public FileChannel(final InputStream source, final OutputStream destination) {
        this(source, destination, BUFFER_SIZE, true);
    }

    public FileChannel(final InputStream source,
            final OutputStream destination,
            final boolean shouldClose) {
        this(source, destination, BUFFER_SIZE, shouldClose);
    }

    public FileChannel(final InputStream source,
            final OutputStream destination,
            final int bufferSize,
            final boolean shouldClose) {
        this.source = Channels.newChannel(source);
        this.destination = Channels.newChannel(destination);
        this.bufferSize = bufferSize;
        this.shouldClose = shouldClose;
    }

    /**
     * Transfer bytes from source to destination stream.
     */
    public void transfer() {
        // Used Direct byte buffers because this is the most efficient means of performing I/O on the JVM
        final ByteBuffer buffer = ByteBuffer.allocateDirect(this.bufferSize);

        while (read(buffer) != -1) {
            // prepare the buffer to be drained
            buffer.flip();

            // write to the channel
            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()) {
            write(buffer);
        }
    }

    int read(final ByteBuffer byteBuffer) {
        try {
            return this.source.read(byteBuffer);
        } catch (final IOException exception) {
            throw new IllegalStateException("Error reading from source channel", exception);
        }
    }

    private void write(final ByteBuffer byteBuffer) {
        try {
            while (byteBuffer.hasRemaining()) {
                this.destination.write(byteBuffer);
            }
        } catch (final IOException exception) {
            throw new IllegalStateException("Error writing to destination channel", exception);
        }
    }

    @Override
    public void close() {
        if (!this.shouldClose) {
            return;
        }

        IOUtils.closeQuietly(this.source);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.