Java NIO FileChannels 跟踪进度

标签 java web nio filechannel

我正在尝试从网络下载 mp4 文件。我想异步执行并跟踪进度,以便可以将其显示在进度栏中。

我的代码如下所示:

 URLConnection con = url.openConnection();
 ReadableByteChannel rbc = Channels.newChannel(con.getInputStream());
 FileOutputStream fos = new FileOutputStream(file);
 fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);

使用transferFrom创建一个循环,每次仅读取32KB并递增位置是一个很好的做法,还是有更好的方法可以让我跟踪下载进度?我怎么知道何时停止转移?

我刚刚发现你可以通过 HTTP header 字段获取文件大小:

con.getHeaderFields().get("Content-Length").get(0)

现在知道文件大小应该使我能够实现前面提到的循环。

        InputStream in = con.getInputStream();
        long fileSize = Long.parseLong(con.getHeaderFields().get("Content-Length").get(0));
        FileOutputStream fos = new FileOutputStream(file);

        for (long offset = 0; offset < fileSize; offset += transferBytesAtOnce) {
            in.skip(offset);
            ReadableByteChannel rbc = Channels.newChannel(in);
            fos.getChannel().transferFrom(rbc, offset, transferBytesAtOnce);

            System.out.println(offset + "/" + fileSize);
        }

但是,正如我所料,这个解决方案的性能非常糟糕。当将transferBytesAtOnce变量保持在低水平时,它的下载速度非常慢。当使用高值时,它会取消下载。

编辑:Nvm,将跳过替换为

if (offset > 0) {
                in.skip(transferBytesAtOnce);
            }

它现在工作得更好了,但仍然不如不跟踪进度的解决方案那么快。

最佳答案

灵感来自this我写这个类

public class ReadableConsumerByteChannel implements ReadableByteChannel {

    private final ReadableByteChannel rbc;
    private final IntConsumer onRead;

    private int totalByteRead;

    public ReadableConsumerByteChannel(ReadableByteChannel rbc, IntConsumer onBytesRead) {
        this.rbc = rbc;
        this.onRead = onBytesRead;
    }

    @Override
    public int read(ByteBuffer dst) throws IOException {
        int nRead = rbc.read(dst);
        notifyBytesRead(nRead);
        return nRead;
    }

    protected void notifyBytesRead(int nRead){
        if(nRead<=0) {
            return;
        }
        totalByteRead += nRead;
        onRead.accept(totalByteRead);
    }
    @Override
    public boolean isOpen() {
        return rbc.isOpen();
    }

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

然后像这样包裹旧的来使用它

    URLConnection con = new URL(url).openConnection();
    int fileSize = con.getContentLength();
    ReadableByteChannel rbc = Channels.newChannel(con.getInputStream());
    ReadableConsumerByteChannel rcbc = new ReadableConsumerByteChannel(rbc,(b)->{
        System.out.println("Read  "+b +"/"+fileSize);
    });
    FileOutputStream fos = new FileOutputStream(file);
    fos.getChannel().transferFrom(rcbc, 0, Long.MAX_VALUE);

关于Java NIO FileChannels 跟踪进度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30405695/

相关文章:

java - 我如何在 JBoss 中调用 java 服务器类进行 JAAS 身份验证?

jsf - javax.faces.PROJECT_STAGE的使用

java - 使用 java.nio.MappedByteBuffer 时防止 OutOfMemory

java - 针对小文件优化Java的NIO

java - 使用正则表达式在每个单词的末尾附加一个字符

java - android 中推送通知到达后如何更改家庭 Activity 中的通知图标

java - 如何在 Web 应用程序中显示启动进度?

java - Java中为多个文件下载分配磁盘空间

java - 以编程方式将上下文范围的 onCompletion() 添加到 Camel Context

android - 如何在 android 中以编程方式检查 Web 服务响应是否是有效的 json?