SSE 实现大文件断点续传的探讨

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


纠错反馈