断点续传,意思就是从之前打断的位置继续传输。

HTTP协议下实现断点续传的规范

  • 在HTTP协议下,有专门作为发起断点续传和标识是否支持断点续传的字段,具体的字段格式以及含义在HTTP规范有详细介绍:Http范围请求规范

Netty实现断点续传

  • 在Netty中实现断点续传主要使用HttpChunkInput类,对文件进行分片传输,使用Java的RandomAccessFile类对文件的访问位置进行定位。核心逻辑如下
public void download(String path, ChannelHandleContext ctx) {
  // 根据路径获取到对应的文件,相当于打开这个文件
  File f = new File(path);
  // 使用RandomAccessFile,使用只读模式,对这个文件进行具体定位读取。如果用读写模式,则为“rw”
	RandomAccessFile file = new RandomAccessFile(f, "r");
  // 对文件进行分片读取,start开始读取的位置,end读取到的地方,end - start + 1此次文件内容的长度,8192每个分片的大小,file拥有对应权限的RandomAccessFile
  ChunkedFile chunkedFile = new ChunkedFile(file, start, end - start + 1, 8192);
  // 向netty的channelHandleContext写入文件,使用HttpChunkedInput,传送分片的文件
  ctx.writeAndFlush(new HttpChunkedInput(chunkedFile), ctx.newProgressivePromise());
}
  • 但是在具体实现的时候,因为HTTP协议规范的原因,需要对HTTP协议进行额外处理,如果请求头中带有Range字段,表示客户端发起的是一个范围请求,就需要读取对应的范围,那么对应的start和end,就需要和Range的范围对应
// 解析HTTP请求头,拿到Range的参数,一般来说,Range的格式都是使用单一范围,即0-xxx格式,多重范围很少见,鄙人也没见过,也没想到多重范围的使用场景
String[] ranges = parseHead(request);
            if (ranges.length > 1) {
                sendAndCleanupConnection(ctx, new DefaultFullHttpResponse(HTTP_1_1, REQUESTED_RANGE_NOT_SATISFIABLE, Unpooled.EMPTY_BUFFER));
                return;
            }
            for (String string : ranges) {
                String[] r = string.split("-");
                start = Integer.parseInt(r[0]);
                if (r.length > 1) {
                    end = Integer.parseInt(r[1]);
                    if (Integer.parseInt(r[1]) == start) {
                        end = contentLength;
                    }
                } else {
                  // 这里的contentLength,其实就是读取的文件的总长度。
                    end = contentLength;
                }
            }
  • 一般来说,在实现断点续传功能的时候,完整做法是对文件进行分片,以分片为单位进行传输,自己保存具体正在传输的分片和断点位置,每次重新请求从保存的断点位置开始。

文中代码完整Demo

不过鄙人写的比较简单,直接从HTTP头中获取参数进行文件读取位置定位,实际执行的过程中出现了有意思的现象,使用迅雷访问服务器下载文件,有时候断点续传功能正常,有时候则不正常,具体情况表现为请求实际上带了Range头,文件的读取也使用了正确offset,但是传输的字节永远从头开始,多次暂停再下载,最后导致下载到99.9%的时候就卡住,最后一小部分内容传输不过来,一直显示传输完成,也没找到原因。但是使用浏览器就下载正常。

Q.E.D.