当我们在进行大文件断点下载、在线音视频预览时,我们不希望从头接收文件数据内容,这时候就用到了文件切片传输的功能。
目录
切片下载原理
客户端向服务器提交想要获得的文件段(切片)
服务器识别请求头,理解客户端想要获得的文件段
服务器在将文件内容以流的形式发送给客户端时,通过请求头切片信息(range)来指定读取文件的位置。例如直接读取文件的中间内容。然后将这部分内容写入响应体中。
如何让服务器识别到一个切片传输请求
Range 请求头
例如:bytes=0-
告诉服务器,从0字节一直向后请求资源
当浏览器识别到是一个视频文件资源时,就会自动添加分片的请求头。让我们拖动视频的进度条看看。
可以发现请求的字节从视频文件的中间部分开始了
示范在Java后端服务器中识别切片请求
基于Spring MVC、 SpringBoot v2.3.9.RELEASE
在Controller中,我们可以在请求中获取到HttpServletRequest、HttpServletResponse 对象
调用request对象中的getHeader方法获取range请求头
String range = request.getHeader("Range");
如果客户端进行分片,则range不为null
range的格式有三种
- 限制头部 例如(bytes=884736-)
- 限制尾部 (bytes=-123456)
- 分段截取 (bytes=123456-884736)
使用Chrome自带的视频播放器,使用的是限制头部
服务端如何定位到客户端指定的字节头部
找到客户端要请求的文件资源,并使用RandomAccessFile 只读打开文件
使用RandomAccessFile中的seek方法移动文件读取指针,移动至客户端想要读取的起始字节处(range的首个数值)
将文件内容写到响应体
通过response对象中的getOutputStream()方法,获取到输出流。借助BufferedOutputStream和缓冲区byte数组将文件内容写入流。中途不断记录写入的字节数量,直到写到客户端想要的最大字节数处。
告知客户端当前处于切片状态
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
response.setHeader("Accept-Ranges", "bytes");
状态代码(206),指示服务器已完成资源的部分GET请求。
返回文件的类型格式
//文件类型
String contentType = request.getServletContext().getMimeType(fileName);
response.setContentType(contentType);
如果浏览器(如Chrome)收到响应,且响应内容为video/mp4或audio/mpeg等能在线播放的格式时,就会以预览的方式打开!否则会启用下载模式下载
注意
切片模式可能会导致文件资源无法下载!
当我们直接通过浏览器的get请求去下载一个zip压缩包,压缩包的格式是不能够在线预览的,所以浏览器会去下载。
例如我准备下载一个压缩包
但问题发生了
浏览器提示没有发现文件!
但切换到Safari浏览器使用get下载却能够找到文件
经过了两小时代码的调试与问题排查,找到了原因。
不支持切片的文件,服务端不能返回206状态码给客户端
因此需要进行判断,对不进行预览的文件不返回206 ,而返回200即可正常下载!
感谢
非常感谢Java劝退师、提供的代码参考
SpringBoot SpringMVC文件下载,大文件断点续传,可以实时播放视频,拖动进度条_Java劝退师、的博客-CSDN博客_springboot大文件断点下载