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

相关推荐
jump68020 分钟前
axios
前端
进击的野人21 分钟前
CSS选择器与层叠机制
css·面试
spionbo23 分钟前
前端解构赋值避坑指南基础到高阶深度解析技巧
前端
用户40993225021227 分钟前
Vue响应式声明的API差异、底层原理与常见陷阱你都搞懂了吗
前端·ai编程·trae
开发者小天29 分钟前
React中的componentWillUnmount 使用
前端·javascript·vue.js·react.js
永远的个初学者1 小时前
图片优化 上传图片压缩 npm包支持vue(react)框架开源插件 支持在线与本地
前端·vue.js·react.js
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ1 小时前
npm i / npm install 卡死不动解决方法
前端·npm·node.js
Kratzdisteln1 小时前
【Cursor _RubicsCube Diary 1】Node.js;npm;Vite
前端·npm·node.js
杰克尼1 小时前
vue_day04
前端·javascript·vue.js
明远湖之鱼2 小时前
浅入理解跨端渲染:从零实现 React DSL 跨端渲染机制
前端·react native·react.js