带进度监控的最快 java 文件复制方法是什么?

What is the fastest java file copy method with progress monitoring?

我正在开发一个文件复制应用程序,该应用程序用于将文件从客户端计算机复制到网络文件夹(UNC 路径)。客户端和网络文件夹使用 10Gbps 连接进行连接。传统的 Stream/Buffer 机制最多只能使用 250Mbps。这就是我开始使用 NIO 方法的原因。 Files.copy()transferFrom() 方法都可以使用目前足够的 6Gbps 带宽。但问题是这两种方法都没有提供进展。我肯定需要在我的应用程序中显示文件复制进度。

然后我找到了ReadableByteChannel界面来跟踪上传进度。但是在实现这个之后,上传速度下降到 100Mbps。不确定我是否没有正确实施它。

OS 级复制(Ctrl+C 和 Ctrl+V)适用于 6Gbps 带宽利用率。如何使用带有进度监控的 Java 方法实现同样的效果?

public class AppTest {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        File source = new File(args[0]);
        File dest = new File(args[1] + File.separator + source.getName());
        long startTime = System.currentTimeMillis();
        try {
            if (args[2].equalsIgnoreCase("s")) {
                copyUsingStream(source, dest, args.length > 3 ? Integer.parseInt(args[3]) : 32 * 1024);
            } else if (args[2].equalsIgnoreCase("fp")) {
                copyUsingFileChannelWithProgress(source, dest);
            } else if (args[2].equalsIgnoreCase("f")){
                copyUsingFileChannels(source, dest);
            } else if (args[2].equalsIgnoreCase("j")) {
                copyUsingFilescopy(source, dest);
            } else {
                System.out.println("Unknown copy option.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Completed in " + (System.currentTimeMillis() - startTime));
    }

    private static void copyUsingStream(File source, File dest, int buf_size) throws IOException {
        System.out.println("Copying using feeder code...");
        System.out.println("Buffer Size : " + buf_size);
        FileInputStream sourceFileIS = new FileInputStream(source);
        FileOutputStream srvrFileOutStrm = new FileOutputStream(dest);
        byte[] buf = new byte[buf_size];
        int dataReadLen;
        while ((dataReadLen = sourceFileIS.read(buf)) > 0) {
            srvrFileOutStrm.write(buf, 0, dataReadLen);
        }
        srvrFileOutStrm.close();
        sourceFileIS.close();
    }

    private static void copyUsingFileChannels(File source, File dest)
            throws IOException {
        System.out.println("Copying using filechannel...");
        FileChannel inputChannel = null;
        FileChannel outputChannel = null;
        try {
            inputChannel = new FileInputStream(source).getChannel();
            outputChannel = new FileOutputStream(dest).getChannel();
            outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
        } finally {
            inputChannel.close();
            outputChannel.close();
        }
    }

    private static void copyUsingFilescopy(File source, File dest) throws IOException{
        Files.copy(source.toPath(), dest.toPath());
    }

    interface ProgressCallBack {

        public void callback(CallbackByteChannel rbc, double progress);
    }

    static class CallbackByteChannel implements ReadableByteChannel {

        ProgressCallBack delegate;
        long size;
        ReadableByteChannel rbc;
        long sizeRead;

        CallbackByteChannel(ReadableByteChannel rbc, long sizeRead, long expectedSize, ProgressCallBack delegate) {
            this.delegate = delegate;
            this.sizeRead = sizeRead;
            this.size = expectedSize;
            this.rbc = rbc;
        }

        @Override
        public void close() throws IOException {
            rbc.close();
        }

        public long getReadSoFar() {
            return sizeRead;
        }

        @Override
        public boolean isOpen() {
            return rbc.isOpen();
        }

        @Override
        public int read(ByteBuffer bb) throws IOException {
            int n;
            double progress;
            if ((n = rbc.read(bb)) > 0) {
                sizeRead += n;
                progress = size > 0 ? (double) sizeRead / (double) size * 100.0 : -1.0;
                delegate.callback(this, progress);
            }
            return n;
        }
    }

    private static void copyUsingFileChannelWithProgress(File sourceFile, File destFile) throws IOException {
        ProgressCallBack progressCallBack = new ProgressCallBack() {

            @Override
            public void callback(CallbackByteChannel rbc, double progress) {
//                            publish((int)progress);
            }
        };
        FileOutputStream fos = null;
        FileChannel sourceChannel = null;
        sourceChannel = new FileInputStream(sourceFile).getChannel();
        ReadableByteChannel rbc = new CallbackByteChannel(sourceChannel, 0, sourceFile.length(), progressCallBack);
        fos = new FileOutputStream(destFile);
        fos.getChannel().transferFrom(rbc, 0, sourceFile.length());
        if (sourceChannel.isOpen()) {
            sourceChannel.close();
        }
        fos.close();
    }

}

在一个循环中使用 transferFrom(),其中的大块大小仍然小于文件大小。您必须在此处权衡速度以获取进度指示。您可能希望将块至少设为 1Mb 以保持速度。