SSE 实现大文件断点续传的探讨
在前端开发中,文件上传是一个很常见的需求,但是当需要上传大文件时,一次性上传往往会出现问题,常常会出现上传失败、上传速度慢等问题。为了解决这些问题,需要使用断点续传技术。本文将介绍如何使用 SSE(Server-Sent Events)实现大文件的断点续传。
一、什么是 SSE
SSE 是 Web API 的一部分,它利用了浏览器的事件源机制(EventSource)来实现服务器向客户端推送事件的功能。这种方式不同于传统的轮询技术,它可以在服务器端主动向客户端发送未编号的数据流,使 Web 应用程序更加简单和高效。SSE 是一种单向流协议,客户端的响应速度通常很快,同时也非常省电。
二、SSE 的应用场景
SSE 的应用场景非常广泛,可以用于推送实时消息、聊天室、位置服务等。在文件上传方面,SSE 尤其适用于需要断点续传的情况。因为 SSE 可以创建一个长期连接,通过发送数据块来实现文件读取,而且不会因为传输大文件而卡死或者导致页面崩溃。
三、SSE 实现大文件断点续传的代码示例
下面我们来看一个具体的示例,实现大文件的断点续传。前端代码如下:
let startSize = 0; // 已上传的文件大小 let file = null; const eventSource = new EventSource("/upload"); eventSource.onmessage = function (event) { console.log(event.data); // 打印服务端返回的数据 const response = JSON.parse(event.data); switch (response.type) { case "init": startSize = response.size; break; case "progress": const percent = parseInt((startSize + response.loaded) / file.size * 100); console.log("上传进度:" + percent + "%"); break; case "finished": console.log("上传完成!"); break; case "error": console.log(response.message); break; } }; document.getElementById("uploadBtn").onclick = function () { const formData = new FormData(); file = document.getElementById("fileInput").files[0]; formData.append("file", file); formData.append("startSize", startSize); const xhr = new XMLHttpRequest(); xhr.open("POST", "/upload"); xhr.upload.onprogress = function (event) { if (event.lengthComputable) { const percent = parseInt((startSize + event.loaded) / file.size * 100); console.log("上传进度:" + percent + "%"); } }; xhr.onreadystatechange = function () { if (xhr.readyState === 4) { console.log(xhr.responseText); } }; xhr.send(formData); };
在服务器端,我们需要在 Express 框架中配置 SSE。代码如下:
const express = require("express"); const fs = require("fs"); const app = express(); app.use(express.static("public")); app.get("/upload", function (req, res) { res.setHeader("Cache-Control", "no-cache"); res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Connection", "keep-alive"); const startSize = parseInt(req.headers["last-event-id"]) || 0; const filePath = "public/uploads/001.zip"; fs.stat(filePath, function (err, stats) { if (err) { res.write("event: error\ndata: 文件不存在!\n\n"); res.end(); return; } res.write("retry: 10000\n\n"); res.write("event: init\ndata: " + JSON.stringify({ size: stats.size }) + "\n\n"); const stream = fs.createReadStream(filePath, { start: startSize, end: stats.size - 1 }); stream.on("data", function (chunk) { const loaded = startSize + stream.bytesRead; res.write("event: progress\ndata: " + JSON.stringify({ loaded }) + "\n\n"); }); stream.on("end", function () { res.write("event: finished\ndata: 上传完成!\n\n"); res.end(); }); }); }); app.post("/upload", function (req, res) { req.on("data", function (data) { // 进行文件上传逻辑 }); }); app.listen(3000, function () { console.log("Server running at http://localhost:3000"); });
以上代码实现了一个简单的上传功能,通过 SSE 实现了断点续传。在前端代码中,我们定义了 startSize
变量,用于记录已上传的文件大小,每次上传时都将它作为 header 发送到服务器端。在服务器端,我们使用 fs.createReadStream()
方法将文件流传输到客户端。每传输一次文件块,就发送一条消息前端。同时,我们定义了 retry
参数,用于设置重新连接的时间间隔。这样一来,即使客户端和服务器之间出现短暂的网络问题,也不影响文件的上传。
四、总结
本文介绍了如何使用 SSE 实现大文件的断点续传,我们可以借助 SSE 的单向流特性,以及浏览器的事件源机制,有效地解决了传输大文件时出现的问题。希望这篇文章对大家在前端开发中实现文件上传功能有所帮助。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a8d538add4f0e0ff20f53b