【从前端入门到全栈】Node.js之大文件分片上传

从前端入门到全栈-系列介绍

  1. 你会学到什么?

可能学不到什么东西,该系列是作者本人工作和学习积累,用于复习

  1. 作者介绍

江辰,前网易高级前端工程师

  1. 系列介绍

现在的 Web 前端已经离不开 Node.js,我们广泛使用的 Babel、Webpack、工程化都是基于 Node 的,各个互联网大厂也早已大规模落地 Node 项目。因此,想要成为一名优秀的前端工程师,提升个人能力、进入大厂,掌握 Node.js 技术非常有必要。

Node.js 不仅可以用来完善手头的开发环境,实现减少代码和 HTTP 请求,降低网页请求消耗的时间,提升服务质量。还可以扩展前端工程师的工作领域,用作 HTTP 服务,让前端也能完成一部分后端的工作,减少对后端的依赖,降低沟通成本,提升开发效率。

而且,Node.js 和浏览器的 JavaScript 只是运行时环境不同,编程语言都是 JavaScript ,所以掌握 Node.js 基础对前端工程师来说并不难,难点在于应用。由于浏览器的 JavaScript 主要是负责内容呈现与交互,而 Node.js 应用领域包括工具开发、Web 服务开发和客户端开发,这些都与传统的Web前端领域不一样,用来应对不同的问题。

  1. 适宜人群
  • 对 Node.js 感兴趣的 JavaScript 程序员
  • 希望拓展知识边界,往全栈方向发展的前端工程师

定义

分片上传是把一个大的文件分成若干块,一块一块的传输,这样做的好处就是在文件上传/下载过程中,遇到不可抗力,比如网络中断,服务器异常,或者其他原因导致操作中断;再次操作时,可以从已经上传/下载的部分开始继续上传/下载未完成的部分,而没有必要从头开始上传/下载。

步骤

前端部分

  1. 首先,我们需要使用 HTMLFile API,通过 input 标签获取用户上传的大文件。
  2. 使用 slice 方法将大文件分割成若干个小文件块,即分片。
  3. 再使用 fetchaxios 等方法向服务器发送 POST 请求,上传文件的分片。

后端部分

  1. 使用 express 或类似的框架接收前端上传的文件分片。
  2. 将接收到的文件分片保存到服务器的一个临时位置。
  3. 图所接收到所有的文件分片后,将这些分片合并成一个完整的文件。

流程图

实现

javascript 复制代码
import React, { useState } from 'react';
import axios from 'axios';

const FileUploader = () => {
    const [file, setFile] = useState(null);

    const onSubmit = async event => {
        event.preventDefault();

        if (!file) {
            // 没有文件则直接返回
            return;
        }

        // 以1MB为一个分片
        const blockSize = 1024 * 1024 * 1;
        const fileSize = file.size;

        let start = 0;
        let end = blockSize;

        while (start < fileSize) {
            const chunk = file.slice(start, end);

            const formData = new FormData();
            formData.append('file', chunk, file.name);

            // 分片上传
            await axios.post('/upload', formData);

            start = end;
            end = start + blockSize;
        }

        // 合并
        await axios.get(`/merge?filename=${file.name}`);
    };

    const onChange = event => {
        setFile(event.target.files[0]);
    };

    return (
        <form onSubmit={onSubmit}>
            <input type="file" onChange={onChange} />
            <button type="submit">上传</button>
        </form>
    );
};

export default FileUploader;
javascript 复制代码
const express = require('express');
const fs = require('fs');
const path = require('path');
const multer = require('multer');

const upload = multer({ dest: 'uploads/' });
const app = express();

app.post('/upload', upload.single('file'), (req, res) => {
    const chunk = req.file; // 获取上传的文件分片
    const { originalname } = req.file;
    const chunkDirPath = path.resolve(__dirname, 'chunks');
    const chunkPath = path.resolve(chunkDirPath, originalname);

    // 如果 chunks 文件夹不存在,就创建一个
    if (!fs.existsSync(chunkDirPath)) {
        fs.mkdirSync(chunkDirPath);
    }

    // 将文件分片保存到 chunks 文件夹下
    fs.renameSync(chunk.path, chunkPath);

    res.sendStatus(200);
});

app.get('/merge', (req, res) => {
    const { filename } = req.query;
    const chunkDirPath = path.resolve(__dirname, 'chunks');
    const filePath = path.resolve(__dirname, 'files', filename);

    fs.readdirSync(chunkDirPath).forEach(chunkPath => {
        // 读取每一个文件分片,然后将它们一起写入一个新文件,完成合并
        fs.appendFileSync(filePath, fs.readFileSync(path.resolve(chunkDirPath, chunkPath)));
        // 合并后删除文件分片
        fs.unlinkSync(path.resolve(chunkDirPath, chunkPath));
    });

    // 删除用于保存文件分片的文件夹
    fs.rmdirSync(chunkDirPath);

    res.sendStatus(200);
});

app.listen(8000, () => {
    console.log('Server listening on port 8000');
});

请注意这是一种简化的实现,无法处理有些复杂情况,如并发上传,断点续传,错误恢复等。如果你需要这些功能,可能需要引入更完善的库或服务,如 tus-js-client,Plupload,Resumable.js 或 UpChunk。

需要注意的点

1. 上传进度: 用户上传大文件时,提供一个进度条可以改善用户体验。你可以使用 XMLHttpRequest 或 Fetch 的 API 获得正在上传的进度信息,并实时展示。

2. 错误处理和重试: 你需要处理可能会发生的错误,比如网络问题或者服务器错误。为了避免用户再次上传文件,可以实现重试机制,当某个分片上传失败时,可以重新上传这个分片。

3. 并发控制: 如果你一次上传多个分片,可能会消耗大量的用户网络带宽。你需要控制同时上传分片的数量,比如使用 Promise.all。

4. 断点续传: 用户在上传大文件时,可能会中断,比如网络连接可能会断开,或者用户关闭了页面。你可以实现断点续传,让用户可以在重新打开页面时,继续上次的上传,而不需要从头开始。通常需要后台支持,记录已经上传的分片。

5. 安全性: 文件上传是一个重要的安全问题,你应该检查并限制用户上传的文件类型,防止上传恶意文件。同时,你应该在服务器端再次检查文件。

总结

文章同步更新平台:掘金、CSDN、知乎、思否、博客,公众号(野生程序猿江辰) 我的联系方式,v:Jiang9684,欢迎和我一起学习交流

相关推荐
晓得迷路了1 分钟前
栗子前端技术周刊第 121 期 - Vitest 4.1、Nuxt 4.4、Next.js 16.2...
前端·javascript·vite
kyle~2 分钟前
Electron桌面容器
前端·javascript·electron
隔壁小邓4 分钟前
vue如何拆分业务逻辑
前端·javascript·vue.js
En^_^Joy8 分钟前
Ajax与Axios:现代前端异步请求指南
前端·javascript·ajax
Cobyte12 分钟前
来,实现一个 Mini Claude Code:从底层理解 AI Agent
前端·aigc·ai编程
SuperEugene12 分钟前
Vue3 + Element Plus 表单校验实战:规则复用、自定义校验、提示语统一,告别混乱避坑|表单与表格规范篇
开发语言·前端·javascript·vue.js·前端框架
酉鬼女又兒15 分钟前
零基础快速入门前端JavaScript 浏览器环境输入输出语句全解析:从弹框交互到控制台调试(可用于备赛蓝桥杯Web应用开发赛道)
前端·javascript·职场和发展·蓝桥杯·js
清汤饺子16 分钟前
搞懂 Cursor 后,我一行代码都不敲了《实战篇》
前端·javascript·后端
SuperEugene16 分钟前
Vue3 + Element Plus 表格查询规范:条件管理、分页联动 + 避坑,标准化写法|表单与表格规范篇
开发语言·前端·javascript·vue.js·前端框架
问道飞鱼24 分钟前
【前端知识】React生态你了解多少?
前端·react.js·前端框架·生态