【Node.js】大文件上传

概述

大文件上传通常采用分片上传。如果因为某些原因上传突然中断,解决问题之后可以接着之前的分片上传,而不需要从头开始上传,也就是断点续传。此外还可以利用多个网络连接并行上传多个分片,提高上传速度。

注:前端不能使用 live-server 去启动, live-server 启动会在上传文件时自动刷新页面,阻止默认事件。所以使用 npm i http-server 去启动 http-server -p 9999这里用了 9999 端口。

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <input id="file" type="file">
    <script>
        const file = document.querySelector('#file')
        file.addEventListener('change', e => {
            let file = e.target.files[0]  // file 对象
            console.log(file)
        })
    </script>
</body>
</html>

此时上传一个 map4 文件。

image.png

分片上传。

image.png

完整代码

前端

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<input id="file" type="file">
<script>
    const file = document.querySelector('#file')
    // 2 MB 的文件 我这里 以 1MB 为一个切片
    const chunksFn = (file, size = 1024 * 1024) => {
        const chunks = []
        for (let i = 0; i < file.size; i += size) {
            chunks.push(file.slice(i, i + size))
        }
        return chunks
    }
    const uploadFiles = (chunks) => {
        // 批量上传 Promise.all
        const list = []
        for (let i = 0; i < chunks.length; i++) {
            const formData = new FormData()
            formData.append('index', i)  // 标识
            formData.append('filename', 'videoTest')
            formData.append('file', chunks[i])  // 必须写最后,因为multer读到file,之后的index,filename等(自定义的)就不会处理了
            list.push(fetch('http://localhost:3000/upload', {
                method: 'POST',
                body: formData
            }))
        }
        // 等待所有请求发送完成,通知后端合并切片
        Promise.all(list).then(res => {
            fetch('http://localhost:3000/merge', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    filename: 'videoTest'
                })
            })
            console.log('上传成功')
        })
    }
    file.addEventListener('change', e => {
        // files 对象 底层继承 blob 可以调用 slice 方法进行切割
        let file = e.target.files[0]
        const chunks = chunksFn(file)
        uploadFiles(chunks)
    })
</script>
</body>
</html>

后端

import multer from 'multer'
import express from 'express'
import cors from 'cors'
import fs from 'fs'
import path from "path";

// 初始化 multer
const storage = multer.diskStorage({
    destination: function (req, res, cb) {
        cb(null, 'upload/') // 分片存储的目录
    },
    filename(req, res, cb) {
        cb(null, `${req.body.index}-${req.body.filename}`)
    }
})
const upload = multer({storage})
const app = express()
app.use(cors())
app.use(express.json())
app.post('/upload', upload.single('file'), (req, res) => {
    res.send('ok')
})
app.post('/merge', (req, res) => {
    // 读取目录下面的所有切片
    const uploadDir = path.join(process.cwd(), 'upload')
    const uploadArr = fs.readdirSync(uploadDir)
    // 排序,再进行拼接
    uploadArr.sort((a, b) => a.split('-')[0] - b.split('-')[0])
    const videoDir = path.join(process.cwd(), 'video', `${req.body.filename}.mp4`)
    uploadArr.forEach(item => {
        fs.appendFileSync(videoDir, fs.readFileSync(path.join(uploadDir, item)))
        // 分片合并后就可以清除了
        fs.unlinkSync(path.join(uploadDir, item))
    })
    res.send('ok')
})
app.listen(3000, (req, res) => {
    console.log('3000端口已启动')
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/503325.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

动手学机器学习逻辑斯谛回归+习题

逻辑斯谛函数下的线性模型 现需要用线性模型做分类问题&#xff0c;简单的阶跃函数在阈值处不可导&#xff0c;可导处导数均为0&#xff0c;性质不好 所以把0&#xff0c;1问题转化成P(y0|x)&#xff0c;P(y1|x)的问题&#xff0c;这样就把离散的分类任务变成了求概率分布的回…

ROS传感器图像转换

ros通过摄像头来获得图片&#xff0c;传感器数据类型为sensor_msgs中的Image&#xff0c;具体的数据类型组成&#xff1a; sensor_msgs/Image Documentationhttp://docs.ros.org/en/api/sensor_msgs/html/msg/Image.html但是我们一般使用opencv对图像进行处理&#xff0c;所以…

Python字符串字母大小写变换,高级Python开发技术

寻找有志同道合的小伙伴&#xff0c;互帮互助,群里还有不错的视频学习教程和PDF电子书&#xff01; ‘’’ demo ‘tHis iS a GOod boOK.’ print(demo.casefold()) print(demo.lower()) print(demo.upper()) print(demo.capitalize()) print(demo.title()) print(dem…

Python字典操作

假设我们有一个学生信息数据库&#xff0c;其中存储了每个学生的姓名、年龄、性别和成绩。我们可以使用字典来表示每个学生的信息&#xff0c;并将所有学生存储在一个字典列表中。 设计者&#xff1a;ISDF 版本&#xff1a;v1.0 日期&#xff1a;03/29/2024# 定义学生信息字典列…

如何划分训练集、测试集、验证集

训练集、测试集和验证集是在机器学习和数据科学中常用的术语&#xff0c;用于评估和验证模型的性能。它们通常用于监督学习任务中。 1. 训练集&#xff08;Training Set&#xff09;&#xff1a;训练集是用于训练机器学习模型的数据集。在训练期间&#xff0c;模型使用训练集中…

小狐狸ChatGPT付费AI创作系统V2.8.0独立版 + H5端 + 小程序前端

狐狸GPT付费体验系统的开发基于国外很火的ChatGPT&#xff0c;这是一种基于人工智能技术的问答系统&#xff0c;可以实现智能回答用户提出的问题。相比传统的问答系统&#xff0c;ChatGPT可以更加准确地理解用户的意图&#xff0c;提供更加精准的答案。同时&#xff0c;小狐狸G…

图形推理 总结

原则 1.图形相似且元素基本不变&#xff1a;此时多考虑图形的位置移动规律&#xff0c;如平移、旋转、翻转等。 2.图形相似但元素有同有异&#xff1a;这种情况下常考组合叠加-去异存同、去同存异等;元素遍历;部分传递等。 3.图形相异但较规则&#xff1a;常考对称、直曲性、…

JDK8的下载安装与环境变量配置教程

前言 官网下载&#xff1a;Java Archive Downloads - Java SE 8u211 and later 现在应该没人用32位的系统了吧&#xff0c;直接下载Windows x64 Installer jdk-8u391-windows-x64.exe 一、安装JDK 1. 打开jdk-8u391-windows-x64.exe 2. 直接下一步 3. 这个地方不要动他&…

神经网络与深度学习(一)

线性回归 定义 利用数理统计中回归分析&#xff0c;来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法 要素 训练集&#xff08;训练数据&#xff09;输出数据拟合函数数据条目数 场景 预测价格&#xff08;房屋、股票等&#xff09;、预测住院时间&#…

深入探讨Docker in Docker:原理与实战指南

在软件开发和部署中&#xff0c;容器化技术已经成为一个不可或缺的工具。而在使用Docker进行容器化时&#xff0c;有时可能会遇到需要在一个Docker容器中运行另一个Docker容器的情况&#xff0c;这就是所谓的"Docker in Docker"&#xff08;简称DinD&#xff09;。本…

java数组与集合框架(二)-- 集合框架,Iterator迭代器,list

集合框架&#xff1a; 用于存储数据的容器。 Java 集合框架概述 一方面&#xff0c;面向对象语言对事物的体现都是以对象的形式&#xff0c;为了方便对多个对象的操作&#xff0c;就要对对象进行存储。另一方面&#xff0c;使用Array存储对象方面具有一些弊端&#xff0c;而…

Redis从入门到精通(一)Redis安装与启动、Redis客户端的使用

文章目录 写在最前第1章 Redis概述1.1 初识Redis1.1.1 NoSQL1.1.2 Redis的特点与优势 1.2 安装Redis1.2.1 安装依赖库1.2.2 安装Redis1.2.3 启动Redis1.2.3.1 默认启动1.2.3.2 指定配置启动1.2.3.3 开机自启 1.3 Redis客户端1.3.1 命令行客户端1.3.2 图形化桌面客户端1.3.3 编程…

RVM安装Ruby笔记(Mac)

环境 硬件&#xff1a;Macbook Pro 系统&#xff1a;macOS 14.1 安装公钥 通过gpg安装公钥失败&#xff0c;报错如下&#xff1a; 换了几个公钥地址&#xff08;hkp://subkeys.pgp.net&#xff0c;hkp://keys.gnupg.net&#xff0c;hkp://pgp.mit.edu&#xff09;&#xff0c;…

Mysql or与in的区别

创建一个表格 内涵一千万条数据 这张表中&#xff0c;只有id有建立索引&#xff0c;且其余都没有 测试1&#xff1a;使用or的情况下&#xff0c;根据主键进行查询 可以看到根据主键id进行or查询 花费了30-114毫秒&#xff0c;后面30多毫秒可能是因为Mysql的Buffer Pool缓冲池的…

图论做题笔记:dfs

Leetcode - 797&#xff1a;所有可能的路径 题目&#xff1a; 给你一个有 n 个节点的 有向无环图&#xff08;DAG&#xff09;&#xff0c;请你找出所有从节点 0 到节点 n-1 的路径并输出&#xff08;不要求按特定顺序&#xff09; graph[i] 是一个从节点 i 可以访问的所有节…

深入理解数据结构(1):复杂度详解

文章主题&#xff1a;复杂度详解&#x1f331;所属专栏&#xff1a;深入理解数据结构&#x1f4d8;作者简介&#xff1a;更新有关深入理解数据结构知识的博主一枚&#xff0c;记录分享自己对数据结构的深入解读。&#x1f604;个人主页&#xff1a;[₽]的个人主页&#x1f525;…

Intellij IDEA / Android studio 可持续开发笔记

Intellij 的Java/安卓工具链有着一种不可持续性&#xff0c;这种不可持续性体现在多个方面。 首先是不可持续运行。IDEA 使用时间越长&#xff0c;内存占用越大&#xff0c;从不主动释放。运行时间越长&#xff0c;日志越多&#xff0c;从不主动清理。 然后是不完整的开源&am…

java多线程——概述,创建方式及常用方法

前言&#xff1a; 学习到多线程了&#xff0c;整理下笔记&#xff0c;daydayup!!! 多线程 什么是线程 线程&#xff08;Thread&#xff09;是一个程序内部的一条执行流程。若程序只有一条执行流程&#xff0c;那这个程序就是单线程的程序。 什么是多线程 多线程是指从软硬件上…

预处理详解(二)-- 条件编译 - 头文件包含 - ##和#运算符

目录 一.##和#运算符1.#运算符&#xff08;字符串化&#xff09;2.##运算符&#xff08;粘合符&#xff09; 二.条件编译&#xff08;很重要&#xff09;三.命名约定1.宏名的命名2.函数的命名 四.#undef(用于移除一个宏定义)五.命名行约定六.头文件被包含的方式1.本地文件包含2…

Adaboost集成学习 | Matlab实现基于ELM-Adaboost极限学习机结合Adaboost集成学习时间序列预测(股票价格预测)

目录 效果一览基本介绍模型设计程序设计参考资料效果一览 基本介绍 基于ELM-Adaboost极限学习机结合Adaboost集成学习时间序列预测(股票价格预测) 单变量时间序列单步预测。 ELM(Extreme Learning Machine,极限学习机)和AdaBoost(Adaptive Boosting,自适应提升)都是机…