我正在尝试从网上下载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);
}
现在它的工作情况有点好,但仍然没有跟踪不跟踪进度的解决方案那么快。
答案 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);