一、情况说明
手机端请求后端接口,返回视频流给前端,然后在手机端进行播放,手机端需要适配ios和安卓,后端在开发好接口后在浏览器、postman调用接口时都能正常播放,安卓客户端也可以正常播放,但是ios播放失败。
刚开始觉得是网速太慢的原因,换了一个更小的视频,仍然不行,然后查了下资料,说是视频编码的问题,然后又换了下视频编码,仍然不行,最后经过测试,发现ios客户端使用video标签解析视频时会根据响应的请求头进行读取数据,响应头需要包含文件大小,如果没有就无法解析视频,但是在安卓客户端和浏览器上是没有这种问题的,所以需要兼容ios。
二、条件说明
视频文件是通过手机拍摄,没有经过任何处理的mp4文件。
后端是springboot项目,将视频放在resource资源目录下。
前端需要适配ios和安卓客户端,使用video标签进行解析。
三、后端代码
- 首先需要读取类路径下的指定资源文件,转换为二进制流,设置好响应头,再返回。
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
@GetMapping("/video/{fileName}")
public void video2(@PathVariable String fileName, HttpServletResponse response) throws IOException {
//读取类路径下的资源文件
ClassPathResource resource = new ClassPathResource("file/" + fileName);
//转为File对象
File file = resource.getFile();
//获取文件大小,因为file.length()返回的是long类型,单位是B,但是响应头是int类型,所以需要强转为int类型
//int类型的大小为2147483647,折算下来,文件大小不能超过1GB,可以这样做,如果大小超过1GB,那么需要分段读取返回了
int fileLength = (int)file.length();
//将文件对象转为输入流
FileInputStream input = new FileInputStream(file);
//设置响应头,inline:表示在线预览,后面是文件的名字
response.setHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(fileName, "UTF-8"));
//设置文件的大小 (ios不设置无法正常播放视频)
response.setHeader("Content-Length", String.valueOf(fileLength));
//文件格式
response.setContentType("video/mp4");
//将二进制流返回给前端
FileCopyUtils.copy(input, response.getOutputStream());
}
四、总结
- 安卓和浏览器适配做得很好,只需要设置好文件类型和
Content-Disposition
,最后返回文件的二进制流即可。
但是ios客户端还需要设置文件大小才能正确播放。
我只做了MP4格式的视频,其他格式的视频暂时还没有试过,百度看了下,其他视频文件可能会涉及到视频编码的问题。 - 在手机端上播放视频,流量很贵,像我的做法的话都是一次性返回整个文件的二进制流,这样过于暴力,后续优化的时候,可能要设置另外的响应头,进行分段返回。比如响应头中
Content-Range
是获取文件的部分内容。 - 用分段的形式返回文件二进制流的实现方式:分段返回文件二进制流的实现方式