Java NIO FileChannels跟踪进度

时间:2015-05-22 20:52:58

标签: 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标头字段获取文件大小:

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

现在知道filesize应该能够实现前面提到的循环。

        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);
            }

现在它的工作情况有点好,但仍然没有跟踪不跟踪进度的解决方案那么快。

1 个答案:

答案 0 :(得分:1)

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);