什么是 SSE
SSE(Server-Sent Events)是一种基于 HTTP 协议的服务器推送技术,它允许服务器向客户端发送事件流(Event Stream),并通过 JavaScript 的 EventSource API 进行监听和处理。与传统的 AJAX 请求相比,SSE 具有以下优点:
- 实时性更强:服务器可以在任何时候向客户端推送数据,而不需要客户端发送请求。
- 更少的网络流量:SSE 只需要建立一次连接,就可以持续接收服务器推送的数据,而不需要反复发送请求。
- 更好的可靠性:SSE 使用 HTTP 协议,可以利用 HTTP 的一些特性(如重试、断点续传等)来保证数据的可靠性。
在实际开发中,我们通常会遇到需要读取大文件的情况,比如视频、音频、图片等。这时候,如果直接使用 AJAX 请求,会导致以下问题:
- 首次加载时间过长:由于文件过大,需要等待整个文件下载完成才能开始播放。
- 流量浪费:如果用户只播放了文件的一部分,但是 AJAX 请求已经将整个文件下载完成,这会导致不必要的流量浪费。
- 无法实现分段读取:如果用户只想播放文件的一部分,但是 AJAX 请求已经将整个文件下载完成,这会导致无法实现分段读取。
SSE 可以解决以上问题。通过 SSE 技术,我们可以将文件分段发送给客户端,并且客户端可以将已经接收到的数据缓存起来,以便下次继续播放。
下面是一个简单的示例,演示如何使用 SSE 实现客户端缓存分段读取文件:
服务端代码
// javascriptcn.com 代码示例 const http = require('http'); const fs = require('fs'); http.createServer((req, res) => { if (req.url === '/video') { const path = './test.mp4'; const stat = fs.statSync(path); const fileSize = stat.size; const range = req.headers.range; if (range) { const parts = range.replace(/bytes=/, '').split('-'); const start = parseInt(parts[0], 10); const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1; const chunksize = (end - start) + 1; const file = fs.createReadStream(path, { start, end }); const head = { 'Content-Range': `bytes ${start}-${end}/${fileSize}`, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/mp4', }; res.writeHead(206, head); file.pipe(res); } else { const head = { 'Content-Length': fileSize, 'Content-Type': 'video/mp4', }; res.writeHead(200, head); fs.createReadStream(path).pipe(res); } } else { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(` <html> <head> <title>SSE Demo</title> </head> <body> <video controls src="/video"></video> </body> </html> `); } }).listen(3000);
客户端代码
// javascriptcn.com 代码示例 const video = document.querySelector('video'); const source = new EventSource('/sse'); let rangeStart = 0; let rangeEnd = 1024 * 1024 - 1; let loadedEnd = 0; source.addEventListener('message', (event) => { const data = JSON.parse(event.data); const { chunk, start, end } = data; if (start > loadedEnd + 1) { // 当前数据不连续,需要重新加载 rangeStart = start; rangeEnd = rangeStart + 1024 * 1024 - 1; loadedEnd = rangeStart - 1; video.currentTime = start; source.close(); source.addEventListener('open', () => { source.send(JSON.stringify({ start: rangeStart, end: rangeEnd })); }); } else { // 当前数据连续,可以直接播放 const blob = new Blob([chunk], { type: 'video/mp4' }); const url = URL.createObjectURL(blob); const videoSourceBuffer = videoSourceBuffer || video.msSourceBuffer || video.webkitSourceBuffer; if (videoSourceBuffer) { videoSourceBuffer.appendBuffer(chunk); } else { video.src = url; } loadedEnd = end; } }); source.addEventListener('open', () => { source.send(JSON.stringify({ start: rangeStart, end: rangeEnd })); }); video.addEventListener('timeupdate', () => { const currentTime = video.currentTime; if (currentTime > loadedEnd) { // 当前播放时间超过已经加载的时间,需要重新加载 rangeStart = loadedEnd + 1; rangeEnd = rangeStart + 1024 * 1024 - 1; loadedEnd = rangeStart - 1; source.close(); source.addEventListener('open', () => { source.send(JSON.stringify({ start: rangeStart, end: rangeEnd })); }); } });
在上面的示例中,服务端代码会监听 /video
请求,并根据客户端发送的 range
头信息,返回对应的文件片段。客户端代码会使用 SSE 技术向服务端发送请求,并根据服务端返回的数据,分段缓存文件并播放。
具体来说,客户端代码会将文件分为大小为 1MB 的多个片段,并向服务端发送请求,请求对应的片段。服务端会返回对应的片段,并在返回的数据中包含该片段的起始位置和结束位置。客户端会根据这些信息,判断当前返回的数据是否连续,并将连续的数据缓存起来。当用户播放到已经缓存的最后一部分时,客户端会向服务端发送请求,请求下一个片段。
总结
SSE 技术可以有效地解决读取大文件的问题,并且可以实现客户端缓存分段读取文件的功能。通过本文的介绍,读者可以了解 SSE 的基本原理和使用方法,并可以根据示例代码进行实际开发。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6572c368d2f5e1655dbb948d