赞
踩
1.场景描述
服务端上传MP4视频文件,iOS客户端通过URL播放该视频文件。提供视频接口,可以进行视频下载或者直接播放,但是iOS手机无法播放,且PC端safari浏览器也无法播放。
2.问题描述
安卓手机可以正常播放视频,iOS手机无法播放,且PC段safari浏览器也无法播放。
3.问题分析
(1)safari不支持整个文件流,服务器必须支持分段请求。
(2)safari对于文件流的请求需要包含一个请求头Range, 和一个响应头Content-Range
4.针对问题分析,进行文件分段传输,以下代码已经验证,可行,代码如下:
- package com.example.yonyou.dyp.com;
-
- import org.springframework.core.io.FileSystemResource;
- import org.springframework.core.io.Resource;
- import org.springframework.http.HttpHeaders;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.MediaType;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestHeader;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
- import javax.servlet.http.HttpServletRequest;
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
-
- /**
- * @description: iOS手机无法播放,且PC端safari浏览器也无法播放问题修复
- * @author Lancy
- * @date: 2023/12/8 17:11
- */
- @RestController
- @RequestMapping("/videos")
- public class VideoController {
-
- @GetMapping("/{videoFileName}")
- public ResponseEntity<byte[]> streamVideo(
- @RequestHeader(value = "Range", required = false) String rangeHeader,
- HttpServletRequest request
- ) throws IOException {
-
- String filePath = "D:/video/20230801_093526.mp4";
-
- // 获取视频文件的Resource对象(假设convertToLocalResource提供了这个方法)
- Resource videoResource = convertToLocalResource(filePath);
-
- // 处理Range请求
- if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
- return handleRangeRequest(videoResource, rangeHeader);
- } else {
- return handleFullRequest(videoResource);
- }
- }
-
- private ResponseEntity<byte[]> handleRangeRequest(Resource videoResource, String rangeHeader) throws IOException {
- // 解析Range请求头
- long[] range = parseRange(rangeHeader, videoResource.contentLength());
-
- // 获取视频的部分数据
- byte[] videoBytes = getPartialVideo(videoResource, range[0], range[1]);
-
- // 设置Content-Range头部
- HttpHeaders headers = createRangeHeaders(videoBytes.length, range[0], range[1], videoResource.contentLength());
-
- return new ResponseEntity<>(videoBytes, headers, HttpStatus.PARTIAL_CONTENT);
- }
-
- private ResponseEntity<byte[]> handleFullRequest(Resource videoResource) throws IOException {
- // 获取完整视频的数据
- byte[] videoBytes = getFullVideo(videoResource);
-
- // 设置Content-Range头部
- HttpHeaders headers = createFullHeaders(videoBytes.length, videoResource.contentLength());
-
- return new ResponseEntity<>(videoBytes, headers, HttpStatus.OK);
- }
-
- private long[] parseRange(String rangeHeader, long contentLength) {
- // 解析Range请求头
- String[] range = rangeHeader.substring(6).split("-");
- long start = Long.parseLong(range[0]);
- long end = range.length==1 || range[1].isEmpty() ? contentLength - 1 : Long.parseLong(range[1]);
- return new long[]{start, end};
- }
-
- private byte[] getPartialVideo(Resource videoResource, long start, long end) throws IOException {
- // 获取部分视频数据
- try (InputStream videoStream = videoResource.getInputStream()) {
- long length = end - start + 1;
- byte[] videoBytes = new byte[(int) length];
- videoStream.skip(start);
- videoStream.read(videoBytes, 0, (int) length);
- return videoBytes;
- }
- }
-
- private HttpHeaders createRangeHeaders(long contentLength, long start, long end, long totalLength) {
- // 设置Content-Range头部
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.parseMediaType("video/mp4"));
- headers.setContentLength(contentLength);
- headers.add("Content-Range", "bytes " + start + "-" + end + "/" + totalLength);
- return headers;
- }
-
- private byte[] getFullVideo(Resource videoResource) throws IOException {
- // 获取完整视频的数据
- try (InputStream videoStream = videoResource.getInputStream()) {
- byte[] videoBytes = new byte[(int) videoResource.contentLength()];
- videoStream.read(videoBytes, 0, videoBytes.length);
- return videoBytes;
- }
- }
-
- private HttpHeaders createFullHeaders(long contentLength, long totalLength) {
- // 设置Content-Range头部
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.parseMediaType("video/mp4"));
- headers.setContentLength(contentLength);
- headers.add("Content-Range", "bytes 0-" + (contentLength - 1) + "/" + totalLength);
- return headers;
- }
-
- public Resource convertToLocalResource(String filePath) {
- File file = new File(filePath);
- if (file.exists() && file.isFile()) {
- return new FileSystemResource(file);
- } else {
- throw new IllegalArgumentException("File does not exist or is not a regular file: " + filePath);
- }
- }
-
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
5.使用上述方案可以实现各环境的视频嵌套播放,已经验证过,可以直接用,各位根据自己的代码稍作调整即可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。