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 开发中将会发挥更加重要的作用。

相关推荐
无处不在的海贼2 小时前
小明的Java面试奇遇之发票系统相关深度实战挑战
java·经验分享·面试
秋名山大前端2 小时前
Chrome GPU 加速优化配置(前端 3D 可视化 / 数字孪生专用)
前端·chrome·3d
今天不要写bug2 小时前
antv x6实现封装拖拽流程图配置(适用于工单流程、审批流程应用场景)
前端·typescript·vue·流程图
luquinn2 小时前
实现统一门户登录跳转免登录
开发语言·前端·javascript
用户21411832636023 小时前
dify案例分享-5分钟搭建智能思维导图系统!Dify + MCP工具实战教程
前端
augenstern4163 小时前
HTML(面试)
前端
excel3 小时前
前端常见布局误区:1fr 为什么撑爆了我的容器?
前端
烛阴3 小时前
TypeScript 类型魔法:像遍历对象一样改造你的类型
前端·javascript·typescript
vayy3 小时前
uniapp中 ios端 scroll-view 组件内部子元素z-index失效问题
前端·ios·微信小程序·uni-app
专注API从业者3 小时前
基于 Node.js 的淘宝 API 接口开发:快速构建异步数据采集服务
大数据·前端·数据库·数据挖掘·node.js