HTTP 断点续传与大文件上传,现在面试必问吧

一、引言

在 Web 应用开发中,文件上传是一个常见的需求。而当涉及到上传大文件时,传统的一次性上传方式可能会面临诸多问题,比如网络不稳定导致上传失败需要重新上传,耗费大量时间和带宽资源等。这时,HTTP 断点续传技术就显得尤为重要,它能够让用户在上传大文件时,即使出现网络中断等情况,也无需从头开始,大大提高了上传的效率和用户体验。

二、HTTP 断点续传原理

HTTP 协议提供了Range请求头来支持断点续传功能。Range请求头用于指定请求资源的某个范围,服务器在接收到包含Range请求头的请求后,会根据指定的范围返回对应的部分资源。

例如,当我们请求一个文件时,可以使用类似这样的Range请求头:

ini 复制代码
Range: bytes=500-999

这表示请求文件从第 500 个字节到第 999 个字节的内容。服务器接收到该请求后,会返回状态码206 Partial Content,表示返回的是部分内容,并在响应头中包含Content-Range字段,告知客户端返回的内容范围,如:

css 复制代码
Content-Range: bytes 500-999/10000

其中10000表示文件的总字节数。

三、大文件上传面临的问题

(一)网络不稳定

在上传大文件过程中,网络波动、信号弱等情况都可能导致上传中断。如果没有断点续传功能,用户只能重新开始上传,这对于大文件来说是非常耗时且令人沮丧的。

(二)内存占用

一次性读取整个大文件到内存中进行上传,会占用大量的内存资源,可能导致应用程序性能下降甚至崩溃。

(三)服务器压力

大文件上传会给服务器带来较大的负载,尤其是在高并发场景下,可能影响服务器的正常运行。

四、实现 HTTP 断点续传与大文件上传

(一)前端实现

在前端,我们可以使用BlobFormData对象来实现大文件的分块上传。

  1. 文件分块 通过Blob.slice()方法将大文件分割成多个小块,例如:
javascript 复制代码
const file = document.getElementById('fileInput').files[0];
const chunkSize = 1024 * 1024; // 1MB 一块
const chunks = [];
for (let i = 0; i < file.size; i += chunkSize) {
    chunks.push(file.slice(i, i + chunkSize));
}
  1. 上传分块 使用XMLHttpRequestFetch API发送每个分块,设置Range请求头:
javascript 复制代码
const uploadChunk = (index) => {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', '/upload', true);
        const start = index * chunkSize;
        const end = Math.min(start + chunkSize, file.size);
        xhr.setRequestHeader('Range', `bytes=${start}-${end - 1}`);
        xhr.onload = () => {
            if (xhr.status === 200 || xhr.status === 206) {
                resolve();
            } else {
                reject(new Error(`Upload chunk ${index} failed with status ${xhr.status}`));
            }
        };
        xhr.onerror = (error) => {
            reject(error);
        };
        const formData = new FormData();
        formData.append('file', chunks[index]);
        xhr.send(formData);
    });
};
  1. 断点续传逻辑 记录已经成功上传的分块,在重新上传时跳过已上传的部分:
javascript 复制代码
let uploadedChunks = []; // 记录已上传的分块索引
// 假设从服务器获取已上传分块信息
// uploadedChunks = getUploadedChunksFromServer(); 
for (let i = 0; i < chunks.length; i++) {
    if (!uploadedChunks.includes(i)) {
        await uploadChunk(i);
        uploadedChunks.push(i);
    }
}

(二)后端实现(以 Node.js 为例)

后端需要接收前端上传的分块,并将其合并成完整的文件。同时,要处理Range请求头,返回正确的响应。

  1. 接收分块并合并
javascript 复制代码
const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
const port = 3000;

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'));

app.post('/upload', (req, res) => {
    const file = req.files.file;
    const { range } = req.headers;
    const [_, start, end] = range.match(/bytes=(\d+)-(\d+)/).map(Number);
    const fileName = 'uploadedFile';
    const filePath = path.join(__dirname, fileName);
    const writeStream = fs.createWriteStream(filePath, { start, end });
    file.stream.pipe(writeStream);
    writeStream.on('finish', () => {
        writeStream.close(() => {
            res.status(200).send('Chunk uploaded successfully');
        });
    });
});

app.listen(port, () => {
    console.log(`Server running on port ${port}`);
});
  1. 处理续传请求 在服务器端,需要记录已上传的分块信息,以便在接收到续传请求时,能够正确处理。可以使用数据库或文件等方式记录。当接收到续传请求时,检查已上传的分块,跳过已上传部分,继续接收新的分块。

五、总结

HTTP 断点续传与大文件上传技术,通过巧妙利用 HTTP 协议的特性,解决了大文件上传过程中的诸多难题。在前端和后端的协同配合下,实现了高效、稳定的大文件上传,提升了用户体验,同时也减轻了服务器的压力。随着互联网应用对文件上传需求的不断增加,这种技术在未来的 Web 开发中将会发挥更加重要的作用。

相关推荐
mCell10 小时前
GSAP ScrollTrigger 详解
前端·javascript·动效
gnip10 小时前
Node.js 子进程:child_process
前端·javascript
excel13 小时前
为什么在 Three.js 中平面能产生“起伏效果”?
前端
倔强青铜三14 小时前
苦练Python第46天:文件写入与上下文管理器
人工智能·python·面试
excel15 小时前
Node.js 断言与测试框架示例对比
前端
天蓝色的鱼鱼16 小时前
前端开发者的组件设计之痛:为什么我的组件总是难以维护?
前端·react.js
codingandsleeping16 小时前
使用orval自动拉取swagger文档并生成ts接口
前端·javascript
石金龙17 小时前
[译] Composition in CSS
前端·css
白水清风17 小时前
微前端学习记录(qiankun、wujie、micro-app)
前端·javascript·前端工程化
Ticnix17 小时前
函数封装实现Echarts多表渲染/叠加渲染
前端·echarts