如果直接返回文件流,前端使用video标签播放,则会出现视频无法拖动的问题。
1、demo controller层接口
package com.study.controller;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.Locale;
/**
* 视频播放接口
*
* @Author YL
* @Create 2022/7/27 23:11
* @Version 1.0
*/
@Controller
@RequestMapping("/video")
public class VideoController {
private static final File mp4File = new File("G:\\迅雷下载\\111111.mp4");
/**
* 视频播放接口
* http://localhost:8080/study/video/playVideo
*
* @return
*/
@GetMapping(value = "/playVideo")
@ResponseBody
public void playVideo(HttpServletRequest request, HttpServletResponse response) throws Exception {
ServletOutputStream outputStream = response.getOutputStream();
RandomAccessFile targetFile = new RandomAccessFile(mp4File, "r");
Long offset = 0L; // 偏移量
Long returnLength = mp4File.length(); // 最终返回的数据量大小
String rangeHeader = request.getHeader(HttpHeaders.RANGE); // 格式:Range: bytes=start-end
if (StringUtils.isNotBlank(rangeHeader)) {
// 分段返回从新计算偏移量和数据大小
offset = Long.valueOf(rangeHeader.substring(rangeHeader.indexOf("=") + 1, rangeHeader.indexOf("-")));
returnLength = mp4File.length() - offset;
String end = rangeHeader.substring(rangeHeader.indexOf("-") + 1);
if (StringUtils.isNotBlank(end)) {
returnLength = Long.valueOf(end) - offset + 1;
}
// 设置响应头
long endIndex = (offset + returnLength - 1) > mp4File.length() ? mp4File.length() : (offset + returnLength - 1);
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 断点续传设置206状态码
response.setContentType("video/mp4");
response.setHeader(HttpHeaders.CONTENT_RANGE, String.format(Locale.ROOT, "bytes %d-%d/%d", offset, endIndex, mp4File.length()));
response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
response.setHeader(HttpHeaders.CONTENT_LENGTH, returnLength + "");
} else {
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + mp4File.getName());
response.setHeader("Content-Length", String.valueOf(mp4File.length())); //设置文件长度
response.setHeader("Content-Type", "application/octet-stream");
}
targetFile.seek(offset); //设定文件读取开始位置(以字节为单位)
// 根据范围获取文件
byte[] cache = new byte[1024 * 1024 * 10];
int length;
long countByte = 0L;
boolean isBreak = false;
while (!isBreak && (length = targetFile.read(cache)) != -1) {
countByte += length;
if (countByte > returnLength) {
length = (int) (length - (countByte - returnLength));
isBreak = true;
cache = Arrays.copyOfRange(cache, 0, length);
}
outputStream.write(cache, 0, length);
}
}
}
2、前端使用video标签播放页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<video src="http://localhost:8080/study/video/playVideo" controls="controls" preload="metadata" width="800px" height="500px"></video>
</body>
</html>
3、项目目录结构
4、测试
访问http://localhost:8080/study/video.html
页面