1.最低级的,直接在springboot中读取了整个视频文件,然后把读取到的文件流,写入HttpServletResponse的OutputStream。这种方案问题很大,服务器耗内存,客户端卡顿,因为客户端得把整个视频下载好才能播放。
2.稍好一点的,意识到要使用http的range来实现分片加载,就是客户端无需下载整个视频了,可以拖动进度条,分段请求服务器。但是,网上的教程,都是自己动手实现的,代码逻辑大概是这样:根据客户端要的文件片段,springboot加载本地文件,切分出具体那段文件,返回给客户端。逻辑没问题,就是没必要这么干~因为,springboot自己本身就有这方面的代码。自己写又不稳定,又麻烦。
3.ResourceHttpRequestHandler是springboot加载静态资源的一个类,平时是用来从你resources/statics等目录加载文件的。所以,这个类本身就是支持range请求数据的。我们要做的,就是把要播放的文件File传递给ResourceHttpRequestHandler就行,三五行代码搞定!
@Component
public class NonStaticResourceHttpRequestHandler extends ResourceHttpRequestHandler {
public final static String ATTR_FILE = "NON-STATIC-FILE";
@Override
protected Resource getResource(HttpServletRequest request){
String filePath = (String) request.getAttribute(ATTR_FILE);
return new FileSystemResource(filePath);
}
}
文件上传代码
@Value("${files.upload.path}")
private String fileUploadPath;
@Resource
private NonStaticResourceHttpRequestHandler nonStaticResourceHttpRequestHandler;
/**
* 服务对象
*/
@Resource
private FileService fileService;
@PostMapping("/upload")
public R upload(@RequestBody MultipartFile file) throws IOException {
//获取文件的类型,名称,大小
// String type = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
String type = FileUtil.extName(file.getOriginalFilename());
long size = file.getSize();
//根据当前时间生成名称
Date date = new Date();
SimpleDateFormat dateFormat= new SimpleDateFormat("yyyyMMddhhmmss");
String name = dateFormat.format(date) + StrUtil.DOT + type;
//String fileUUID = IdUtil.fastSimpleUUID() + StrUtil.DOT + type;
java.io.File uploadFile = new java.io.File(fileUploadPath + name);
//如果文件夹不存在则创建
java.io.File parentFile = uploadFile.getParentFile();
if(!parentFile.exists()) {
parentFile.mkdirs();
}
String url;
// 获取文件的md5
String md5 = SecureUtil.md5(file.getInputStream());
// 从数据库查询是否存在相同的记录
File dbFiles = getFileByMd5(md5);
if (dbFiles != null) {
url = dbFiles.getUrl();
} else {
// 上传文件到磁盘
file.transferTo(uploadFile);
url = "http://localhost:8848/file/" + name;
}
//将文件的信息保存到数据库
File saveFile = new File();
saveFile.setUrl(url);
saveFile.setSize(size/1024);
saveFile.setType(type);
saveFile.setName(name);
saveFile.setMd5(md5);
fileService.save(saveFile);
return success(url);
}
/**
* 通过文件的md5查询文件
* @param md5
* @return
*/
private File getFileByMd5(String md5) {
// 查询文件的md5是否存在
LambdaQueryWrapper<File> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(File::getMd5, md5);
List<File> filesList = fileService.list(lambdaQueryWrapper);
return filesList.size() == 0 ? null : filesList.get(0);
}
@GetMapping(value = "/{filename}")
public void download(@PathVariable("filename") String fileName, HttpServletResponse response, HttpServletRequest request) {
try{
String path = fileUploadPath + fileName;
java.io.File file = new java.io.File(path);
if(file.exists()){
request.setAttribute(NonStaticResourceHttpRequestHandler.ATTR_FILE, path);
nonStaticResourceHttpRequestHandler.handleRequest(request, response);
}else {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
}
} catch (ServletException | IOException e) {
}
}
需要导入maven依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
</dependency>
到此,整个springboot播放视频流的功能就实现了,感觉是全网最最简便的了。因为可以根据range返回视频流片段,所以客户端是可以随意拖动进度条,可以倍速啥的播放的。相比把整个视频下载好再播放,这个方案才是yyds!
评论