Java后端实现视频分段渐进式播放

最近在做公司的视频业务,涉及到大的视频文件的上传和播放。针对大文件,无论是上传和下载都需要分片处理,不能像以前处理小图片一样直接将整个文件流上传到服务器,服务器也不能直接响应整个文件给客户端。

大文件的分片上传可以看笔者前面的文章:大文件分片上传前后端实现
这篇文章,主要记录一下,服务端如何将一个大的视频文件做切分,分段响应给客户端,让浏览器可以渐进式的播放。

为什么需要分段播放?

如果一个视频文件很大,例如一部1GB的电影,服务端直接将整个文件响应给客户端是会抛异常的,浏览器也没办法一下子接收这么大的文件,视频播放会出问题。

其次,直接响应一个完整的视频,无疑会浪费服务器的带宽,用户点击播放,很少会完整的观看完视频,可能看一下片头不感兴趣就不看了,亦或是想直接快进到高潮部分,跳过前面的情节等等,服务端应该根据用户的需求,只响应用户真正需要的视频片段就可以了。

服务器带宽是很珍贵的稀缺资源,应该尽可能的节约。

Http请求头Range

Range请求头是HTTP1.1才加入的,它为并行下载以及断点续传提供了技术支持。
如下是一个HTTP请求头示例:

Accept: */*
Accept-Encoding: identity;q=1, *;q=0
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Host: localhost:8080
Range: bytes=0-1024

Range请求头的意思是告诉服务端,这次请求客户端只需要资源的第0-1024个字节的区间数据,服务端只需要响应这部分数据就可以了。

使用<video>标签的src属性指向服务器链接,当服务器响应的HTTP状态码为206时,浏览器会自动开启分段式播放,在每次的HTTP请求头中自动加入Range请求头,服务端只需要根据前端传过来的Range信息截取视频的指定区间来响应即可。

实战

话不多说,直接上后端代码:

/**
 * @author: pch
 * @description: 视频分段播放
 * @date: 2020/10/27
 **/
@RestController
public class VideoController {

	@GetMapping("play")
	public void play(HttpServletRequest request, HttpServletResponse response) throws IOException{
		response.reset();
		File file = new File("/Users/panchanghe/Downloads/阳光电影www.ygdy8.com.北极狗.BD.1080p.国英双语中字.mkv");
		long fileLength = file.length();
		// 随机读文件
		RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");

		//获取从那个字节开始读取文件
		String rangeString = request.getHeader("Range");
		long range=0;
		if (StrUtil.isNotBlank(rangeString)) {
			range = Long.valueOf(rangeString.substring(rangeString.indexOf("=") + 1, rangeString.indexOf("-")));
		}
		//获取响应的输出流
		OutputStream outputStream = response.getOutputStream();
		//设置内容类型
		response.setHeader("Content-Type", "video/mp4");
		//返回码需要为206,代表只处理了部分请求,响应了部分数据
		response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

		// 移动访问指针到指定位置
		randomAccessFile.seek(range);
		// 每次请求只返回1MB的视频流
		byte[] bytes = new byte[1024 * 1024];
		int len = randomAccessFile.read(bytes);
		//设置此次相应返回的数据长度
		response.setContentLength(len);
		//设置此次相应返回的数据范围
		response.setHeader("Content-Range", "bytes "+range+"-"+(fileLength-1)+"/"+fileLength);
		// 将这1MB的视频流响应给客户端
		outputStream.write(bytes, 0, len);
		outputStream.close();
		randomAccessFile.close();

		System.out.println("返回数据区间:【"+range+"-"+(range+len)+"】");
	}
}

读取视频文件的指定位置数据,主要还是用到了JDK提供的java.io.RandomAccessFile类,关于这个类我在前面的文章中有讲到。

在浏览器中直接键入播放地址,视频的请求过程是这样的:
在这里插入图片描述
再来看看后端控制台输出:
在这里插入图片描述

尾巴

利用文件分段下载的特点,除了可以做视频的渐进式播放,还有很多其他的用处。
例如:文件的断点续传、文件多线程并发下载(迅雷就是这么玩的)等。


你可能感兴趣的文章:

要在Java实现视频的在线播放,可以按照以下步骤进行: 1. 在前端页面添加一个视频播放器,并将播放器的src属性设置为视频文件的URL地址。可以使用HTML5中的video标签实现视频播放功能。 2. 在后端编写一个Servlet,用于读取视频文件的字节流,并将字节流写入到HttpServletResponse的输出流中,以便前端页面可以获取到视频文件的内容并播放。 下面是一个简单的示例: 1. 前端页面代码: ```html <video controls> <source src="play?file=video.mp4" type="video/mp4"> Your browser does not support the video tag. </video> ``` 在以上示例中,我们将视频文件名作为参数传递给Servlet,以便Servlet可以读取相应的视频文件。 2. 后端Servlet代码: ```java @WebServlet("/play") public class PlayServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取视频文件名 String fileName = request.getParameter("file"); // 获取视频文件的绝对路径 String filePath = getServletContext().getRealPath("/videos/" + fileName); // 设置响应头,告诉浏览器返回的是视频文件 response.setContentType("video/mp4"); // 读取视频文件的字节流,并将字节流写入到HttpServletResponse的输出流中 try (InputStream in = new FileInputStream(filePath); OutputStream out = response.getOutputStream()) { byte[] buffer = new byte[1024]; int length; while ((length = in.read(buffer)) > 0) { out.write(buffer, 0, length); } } catch (IOException e) { e.printStackTrace(); } } } ``` 在以上示例中,我们首先获取视频文件的绝对路径,然后设置响应头,告诉浏览器返回的是视频文件。最后,我们读取视频文件的字节流,并将字节流写入到HttpServletResponse的输出流中,以便前端页面可以获取到视频文件的内容并播放。 注意:在实际开发中,我们应该将视频文件保存到Web应用程序的根目录下或者外部存储,以便可以通过相对路径或者绝对路径来访问视频文件。同时,在读取视频文件的字节流时,应该使用try-with-resources语句来确保字节流能够被正确关闭,以免出现资源泄漏的问题。
评论 42
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小潘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值