Skip to content

文件上传下载与断点续传

文件上传下载是网络应用中常见的功能,而断点续传则是在处理大文件或不稳定网络环境下确保传输可靠性的关键技术。下面详细介绍这些概念以及实现方法。

文件上传与下载

文件上传

文件上传是指客户端将本地文件发送到服务器的过程。在 Web 开发中,通常通过 HTML 表单结合后端服务来实现:

  • 前端:使用<form>标签配合<input type="file">元素让用户选择文件,并通过 POST 请求提交。
    html
    <form action="/upload" method="post" enctype="multipart/form-data">
      <input type="file" name="file" />
      <input type="submit" value="Upload" />
    </form>
  • 后端:根据所使用的编程语言和框架不同,处理文件接收的方式也有所区别。例如,在 Node.js 中可以使用multer中间件处理文件上传。

文件下载

文件下载则是从服务器获取文件并保存到客户端的过程。同样地,这可以通过 HTTP GET 请求完成:

  • 前端:可以直接通过超链接或者 JavaScript 触发下载。
    html
    <a href="/path/to/file" download="filename">Download</a>
  • 后端:服务器需要设置正确的响应头以告知浏览器这是一个可下载的文件。
    javascript
    // Node.js 示例
    app.get("/download", (req, res) => {
      const filePath = "/path/to/file";
      res.download(filePath); // Express 提供的方法
    });

断点续传

断点续传允许在网络中断或其他原因导致传输中断的情况下,恢复而不是重新开始整个文件的传输过程。这对于大文件传输尤其重要。

实现原理

  1. 范围请求(Range Requests)

    • HTTP 协议支持范围请求,允许客户端请求资源的部分内容而非全部。这通过Range请求头实现。
      http
      Range: bytes=0-1023  # 请求前1024字节
    • 如果服务器支持范围请求,则会返回一个206 Partial Content状态码,并且在响应头中包含Content-Range指示实际返回的内容范围。
      http
      Content-Range: bytes 0-1023/14656   # 表示返回的是总长度为14656字节中的前1024字节
  2. 客户端实现

    • 在下载过程中,如果发生中断,客户端可以根据已下载的部分继续发起新的请求,指定要下载的剩余部分。
    • 对于上传操作,可以利用类似的机制,但实现起来稍微复杂一些,因为大多数服务器默认不支持分块上传。需要特别设计 API 来支持这种模式。
  3. 服务器端支持

    • 确保服务器配置正确以支持范围请求。对于静态文件服务,许多 Web 服务器如 Apache、Nginx 等默认已经支持。
    • 对于动态生成的内容,可能需要手动处理范围请求并在代码中添加相应的逻辑。

示例代码

以下是一个简单的 Node.js+Express 实现文件下载时支持断点续传的例子:

javascript
const express = require("express");
const fs = require("fs");
const path = require("path");

const app = express();

app.get("/download", (req, res) => {
  const filePath = path.resolve(__dirname, "largefile.zip"); // 替换为你的文件路径
  const stat = fs.statSync(filePath);

  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) : stat.size - 1;

    if (start >= stat.size || end >= stat.size) {
      res
        .status(416)
        .send("Requested range not satisfiable\n" + start + " >= " + stat.size);
      return;
    }

    const chunkSize = end - start + 1;
    const fileStream = fs.createReadStream(filePath, { start, end });
    const head = {
      "Content-Range": `bytes ${start}-${end}/${stat.size}`,
      "Accept-Ranges": "bytes",
      "Content-Length": chunkSize,
      "Content-Type": "application/octet-stream",
    };

    res.writeHead(206, head);
    fileStream.pipe(res);
  } else {
    const head = {
      "Content-Length": stat.size,
      "Content-Type": "application/octet-stream",
    };
    res.writeHead(200, head);
    fs.createReadStream(filePath).pipe(res);
  }
});

app.listen(3000, () => console.log("Server running at http://localhost:3000"));

此代码段展示了如何基于客户端提供的Range头部信息来提供部分文件内容,从而支持断点续传功能。对于上传操作,可以参考类似的思想设计适合的 API 接口。

Released under the MIT License.