在 web 开发中,我们经常需要实现在线传送文件的功能,例如上传和下载文件。传统的做法是使用表单提交或者 AJAX 请求,但是这些方式都需要客户端主动发起请求,不太适合实时传送大文件或者长时间传送文件的情况。而 Server-sent Events (SSE) 技术可以解决这个问题,它是一种基于 HTTP 的单向通信技术,可以让服务器主动向客户端推送数据,适合实现在线传送文件的功能。
SSE 原理
SSE 技术基于 HTTP 协议,使用的是长连接 (long-polling) 的方式,也就是客户端向服务器发送一个 HTTP 请求,服务器保持连接不关闭,直到有数据需要推送给客户端时,才返回数据并关闭连接。客户端收到数据后,可以再次发起连接请求,以保持与服务器的通信。这个过程可以看做是服务器向客户端推送数据的过程。
SSE 协议规定了一种特殊的 HTTP 响应格式,包含以下三个部分:
Content-Type
:固定为text/event-stream
,表示返回的是一个事件流。Cache-Control
:为了避免缓存,可以设置为no-cache
。数据部分:由一系列事件组成,每个事件包含一个或多个字段,以
data:
开头,以一个空行结束。例如:data: {"filename": "example.txt", "content": "Hello, world!"}\n\n
这个事件包含了文件名和文件内容两个字段,用 JSON 格式表示,并以两个换行符结束。
客户端可以使用 JavaScript 的 EventSource
对象接收 SSE 数据,例如:
const source = new EventSource('/sse'); source.onmessage = function(event) { console.log(event.data); };
这段代码创建了一个 EventSource
对象,指定了 SSE 数据的来源 /sse
,并定义了 onmessage
回调函数,用于处理接收到的数据。每次服务器推送数据时,onmessage
函数会被调用,传入一个 event
对象,其中的 data
属性就是推送的数据。
实现 SSE 传送文件
使用 SSE 实现在线传送文件的功能,需要在服务器端实现两个部分:接收上传文件的请求和推送下载文件的数据。下面以 Node.js 为例,演示如何实现这两个部分。
接收上传文件的请求
首先,需要实现一个 HTTP POST 请求处理函数,用于接收上传的文件。这个函数需要在接收到文件后,把文件内容推送给所有监听 SSE 的客户端。以下是一个简单的实现:
// javascriptcn.com 代码示例 const http = require('http'); const fs = require('fs'); const server = http.createServer((req, res) => { if (req.method === 'POST' && req.url === '/upload') { let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', () => { const data = JSON.parse(body); const filename = data.filename; const content = data.content; fs.writeFile(filename, content, err => { if (err) { res.statusCode = 500; res.end(err.message); } else { res.end('OK'); } }); res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', }); res.write(`data: {"filename": "${filename}", "content": "${content}"}\n\n`); }); } else { res.statusCode = 404; res.end('Not Found'); } }); server.listen(3000);
这个函数首先判断请求方法和请求路径是否符合要求,如果是,则读取请求体中的 JSON 数据,解析出文件名和文件内容,然后调用 fs.writeFile
写入文件。在写入文件的同时,也向客户端推送数据,使用 SSE 的响应格式,把文件名和文件内容作为一个事件推送出去。
推送下载文件的数据
接下来,需要实现一个 SSE 请求处理函数,用于推送下载的文件数据。这个函数需要读取指定的文件,并把文件内容分块推送给客户端。以下是一个简单的实现:
// javascriptcn.com 代码示例 const http = require('http'); const fs = require('fs'); const server = http.createServer((req, res) => { if (req.method === 'GET' && req.url === '/download') { const filename = 'example.txt'; const stream = fs.createReadStream(filename); res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', }); stream.on('data', chunk => { res.write(`data: ${chunk.toString('base64')}\n\n`); }); stream.on('end', () => { res.end(); }); } else { res.statusCode = 404; res.end('Not Found'); } }); server.listen(3000);
这个函数首先判断请求方法和请求路径是否符合要求,如果是,则读取指定的文件,并创建一个读取流。然后,使用 SSE 的响应格式,把文件内容分块推送给客户端。为了方便传输,这里使用了 Base64 编码。最后,当文件读取完成时,关闭 SSE 连接。
总结
使用 SSE 技术可以实现 web 在线传送文件的功能,可以让服务器主动向客户端推送数据,避免了客户端频繁发起请求的问题。在实现 SSE 传送文件的过程中,需要注意 SSE 的响应格式和数据传输方式,以及服务器端的流控制和错误处理。同时,也可以结合其他技术,例如 WebSocket 和 WebRTC,实现更加高效和实时的在线传送文件的功能。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65591498d2f5e1655d390ed4