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 分钟前
前端工程化:ES6特性
前端·javascript·ecmascript·es6
样子201815 分钟前
PHP 之使用HTMLPurifier过滤XSS
开发语言·前端·php·xss
小阿鑫18 分钟前
程序员最强外设,这才是Coding该有的样子!
前端·程序员·显示器·设计·最强外设
Godiswill20 分钟前
三款简洁免费 AI 抠图去背景网站
前端·javascript·人工智能
haruma sen1 小时前
Spring面试
java·spring·面试
素界UI设计2 小时前
开源网页生态掘金:从Bootstrap二次开发到行业专属组件库的技术变现
前端·开源·bootstrap
潘小安2 小时前
【译】六个开发高手使用的 css 动画秘诀
前端·css·性能优化
前端开发爱好者2 小时前
尤雨溪官宣:Vite 历史性的一刻!超越 Webpack!
前端·javascript·vite
前端开发爱好者2 小时前
Vue3 "抛弃" Axios !用上了 专属请求库!
前端·javascript·vue.js
前端开发爱好者2 小时前
"Lodash" 的终极版!Vue、React 通杀!
前端·javascript·全栈