簡體   English   中英

Java NIO FileChannels 跟蹤進度

[英]Java NIO FileChannels Track Progress

我正在嘗試從網上下載 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)

現在知道文件大小應該讓我能夠實現前面提到的循環。

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

它現在工作得更好一些,但仍然不如不跟蹤進度的解決方案快。

靈感來自這個我寫這個類

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM