springboot中文件上传与下载和流媒体传输ResourceHttpRequestHandler

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!

end
  • 作者:UG666(联系作者)
  • 发表时间:2023-02-10 04:08
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 转载声明:如果是转载博主转载的文章,请附上原文链接
  • 评论