使用 Axios 上传大文件分片上传
背景
在上传大文件时,分片上传是一种常见且有效的策略。由于大文件在上传过程中可能会遇到内存溢出、网络不稳定等问题,分片上传可以显著提高上传的可靠性和效率。通过将大文件分割成多个小分片,不仅可以减少单次上传的数据量,降低内存消耗,还能在遇到网络中断时仅需重传失败的分片,从而提高整体上传的成功率和用户体验。
步骤
安装 Axios
如果你还没有安装 Axios,可以通过 npm 或 yarn 来安装:
npm install axios
# 或者
yarn add axios
获取文件
点击按钮选择文件上传,通过 event
事件对象拿到文件。
<template><div><input type="file" @change="uploadFile"></input></div>
</template>
<script>
import axios from "axios";export default {methods: {uploadFile(event) {const files = event.target.files || event.dataTransfer.files;const file = files[0];console.log('file::: ', file);this.uploadChunks(file, file.name, progress => {console.log(`Upload progress: ${progress * 100}%`);});},},
}
</script>
文件切片并使用 Axios 上传切片:
1. 文件切片:
- 定义
chunkSize
每片大小为 1MB,计算文件需要分割成的总分块数totalChunks
。
2. 循环分块上传:
-
遍历每个分块,计算每个分块的起始位置
start
和结束位置end
。 -
使用
file.slice
方法创建blob
对象表示当前分块。 -
创建
FormData
对象,并添加分块数据及其他元数据(文件名、分块索引、总分块数)。
3. 循环分块上传:
-
使用
axios.post
发送 POST 请求到/upload
接口,携带分块数据。 -
设置请求头
Content-Type
为multipart/form-data
。
4. 循环分块上传:
-
成功上传分块后,记录已上传的分块数量,并调用上传进度的回调函数
onProgress
。 -
设如果上传失败,捕获并记录错误信息。
async uploadChunks(file, fileName, onProgress) {const chunkSize = 1 * 1024 * 1024; // 1MBconst totalChunks = Math.ceil(file.size / chunkSize);let uploadedChunks = 0;for (let i = 0; i < totalChunks; i++) {const start = i * chunkSize;const end = Math.min(start + chunkSize, file.size);const blob = file.slice(start, end);const formData = new FormData();formData.append('file', blob, `${fileName}_${i}`);formData.append('filename', fileName);formData.append('chunkIndex', i.toString());formData.append('totalChunks', totalChunks.toString());try {const response = await axios.post('/upload', formData, {headers: {'Content-Type': 'multipart/form-data',},});console.log(`Chunk ${i} uploaded successfully.`);uploadedChunks++;if (onProgress) {onProgress(uploadedChunks / totalChunks);}} catch (error) {console.error(`Failed to upload chunk ${i}:`, error);}}}
完整代码
<template><div><input type="file" @change="uploadFile"></input></div>
</template><script>
import axios from "axios";export default {data() {},methods: {uploadFile(event) {console.log('event::: ', event);// 获取文件对象const files = event.target.files || event.dataTransfer.files;console.log('files::: ', files);const file = files[0];this.uploadChunks(file, file.name, progress => {console.log(`Upload progress: ${progress * 100}%`);});},async uploadChunks(file, fileName, onProgress) {// 定义每个分片的大小为 1MBconst chunkSize = 1 * 1024 * 1024; // 1MB// 计算总分片数const totalChunks = Math.ceil(file.size / chunkSize);let uploadedChunks = 0;// 遍历所有分片for (let i = 0; i < totalChunks; i++) {// 计算当前分片的起始位置const start = i * chunkSize;// 计算当前分片的结束位置const end = Math.min(start + chunkSize, file.size);// 创建当前分片的 Blob 对象const blob = file.slice(start, end);// 创建表单数据对象const formData = new FormData();// 添加当前分片的文件formData.append('file', blob, `${fileName}_${i}`);// 添加文件名formData.append('filename', fileName);// 添加分片索引formData.append('chunkIndex', i.toString());// 添加总分片数formData.append('totalChunks', totalChunks.toString());try {// 上传分片const response = await axios.post('/upload', formData, {headers: {'Content-Type': 'multipart/form-data',},});console.log(`Chunk ${i} uploaded successfully.`);uploadedChunks++;// 上传进度if (onProgress) {onProgress(uploadedChunks / totalChunks);}} catch (error) {console.error(`Failed to upload chunk ${i}:`, error);}}}}
}
</script><style lang="scss" scoped></style>
注意:
- 使用
FormData
上传文件切片,确保文件部分是以二进制格式上传的。- 设置
Content-Type
为multipart/form-data
。
服务端合并切片
实现原理
1. 搭建服务
-
服务搭建:引入
express
模块,创建了一个express
应用实例app
。 -
设置端口号
PORT
并使用app.listen()
启动express
应用,使其监听指定的端口。
2. 接受并存储切片
-
接收切片:服务端定义了一个
/upload
路由,使用multer
中间件处理上传的文件切片。multer
会将上传的文件暂存到指定的目录(例如uploads/
)。 -
保存切片:服务端根据
filename
和chunkIndex
创建一个临时目录,并将上传的切片移动到该目录中。例如,切片路径可能为uploads/filename/chunkIndex
。 -
创建目录:如果临时目录不存在,服务端会使用 mkdir 方法递归创建目录。
3. 切片合并
-
检测最后一个切片:当接收到的切片索引等于
totalChunks - 1
时,说明这是最后一个切片,触发切片合并操作。 -
读取所有切片:在
mergeChunks
函数中,服务端遍历所有已上传的切片,按顺序读取每个切片的内容。 -
合并切片:将所有切片的内容按顺序拼接成一个完整的文件。这里使用
Buffer.concat
方法将多个Buffer
对象合并成一个。 -
写入合并后的文件:将合并后的文件内容写入到目标目录(例如
merged/
)。 -
删除临时文件:合并完成后,删除所有临时切片文件,释放存储空间。
使用 node
示例
const express = require('express');
const multer = require('multer');
const fs = require('fs');
const path = require('path');
const util = require('util');const app = express();
const upload = multer({ dest: 'uploads/' });// 设置静态文件夹
app.use(express.static('uploads'));// 将 fs 方法转换为 Promise 版本
const mkdir = util.promisify(fs.mkdir);
const rename = util.promisify(fs.rename);
const unlink = util.promisify(fs.unlink);
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);// 文件合并函数
async function mergeChunks(filename, totalChunks) {// 定义存储切片临时文件夹路径const tempDir = `uploads/${filename}/`;// 定义最终合并文件的路径const outputFilePath = `merged/${filename}`;// 创建输出目录await mkdir(path.dirname(outputFilePath), { recursive: true });// 初始化一个空的 Buffer 用于存储合并后的数据let combinedData = Buffer.alloc(0);// 遍历所有切片文件并读取内容for (let i = 0; i < totalChunks; i++) {// 获取每个切片文件的路径const chunkPath = `${tempDir}${i}`;// 读取当前切片文件的内容const chunkData = await readFile(chunkPath);// 合并切片文件的内容追加到 combinedData 中combinedData = Buffer.concat([combinedData, chunkData]);}// 将合并后的数据写入最终的输出文件await writeFile(outputFilePath, combinedData);console.log('File merged successfully.');// 删除临时切片文件for (let i = 0; i < totalChunks; i++) {const chunkPath = `${tempDir}${i}`;try {await unlink(chunkPath);} catch (err) {console.error(`Error deleting chunk ${i}:`, err);}}// 删除临时文件夹try {await rmdir(tempDir, { recursive: true });console.log('Temporary directory deleted successfully.');} catch (err) {console.error('Error deleting temporary directory:', err);}
}// 处理文件上传
app.post('/upload', upload.single('file'), async (req, res) => {const { filename, chunkIndex, totalChunks } = req.body;const chunkPath = `uploads/${filename}/${chunkIndex}`;try {// 创建文件切片目录await mkdir(path.dirname(chunkPath), { recursive: true });// 移动上传的文件到切片目录await rename(req.file.path, chunkPath);console.log(`Chunk ${chunkIndex} saved successfully`);// 如果这是最后一个切片,则合并所有切片if (parseInt(chunkIndex) === parseInt(totalChunks) - 1) {await mergeChunks(filename, totalChunks);}res.status(200).send('Chunk received');} catch (err) {console.error(`Error handling chunk ${chunkIndex}:`, err);res.status(500).send('Internal Server Error');}
});// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {console.log(`Server is running on port ${PORT}`);
});
注意:
- 将
fs
模块的方法转换为Promise
版本,以便防止文件合并顺序错误而导致文件损坏。- 在创建输出文件流时,设置
flags: 'w'
和encoding: null
,确保以二进制格式写入文件。- 在创建输入文件流时,设置
encoding: null
,确保以二进制格式读取文件。
总结
前端:
点击按钮选取文件后,通过事件对象 event
拿到文件并按指定大小(如 1MB)进行分片,使用循环遍历每个分片,创建 blob
对象表示分片,将分片及其相关信息(文件名、分片索引、总分片数)封装到 FormData
对象中,最后使用 axios
发送 POST
请求上传每个分片。
服务端:
服务端通过 API 接口(如 /upload
)接收前端上传的每个分片,解析请求中的 formData
,提取分片数据、文件名、分片索引和总分片数,使用 express
和 multer
接收这些片段,将其保存到临时目录,并在接收到最后一个片段时调用 mergeChunks
函数将所有片段合并成一个完整的文件。合并完成后,删除临时文件。整个过程包括文件切片、上传、保存、合并和清理,确保了大文件的高效传输和处理。
相关文章:
使用 Axios 上传大文件分片上传
背景 在上传大文件时,分片上传是一种常见且有效的策略。由于大文件在上传过程中可能会遇到内存溢出、网络不稳定等问题,分片上传可以显著提高上传的可靠性和效率。通过将大文件分割成多个小分片,不仅可以减少单次上传的数据量,降…...

Nginx+Lua脚本+Redis 实现自动封禁访问频率过高IP
1 、安装OpenResty 安装使用 OpenResty,这是一个集成了各种 Lua 模块的 Nginx 服务器,是一个以Nginx为核心同时包含很多第三方模块的Web应用服务器,使用Nginx的同时又能使用lua等模块实现复杂的控制。 (1)安装编译工具…...
PART 1 数据挖掘概论 — 数据挖掘方法论
目录 数据库知识发掘步骤 数据挖掘技术的产业标准 CRISP-DM SEMMA 数据库知识发掘步骤 数据库知识发掘(Knowledge Discovery in Database,KDD)是从数据库中的大量数据中发现不明显、之前未知、可能有用的知识。 知识发掘流程(Knowledge Discovery Process)包括属性选择…...
Centos安装ffmpeg的方法
推荐第一个,不要自己编译安装,太难了,坑多。 在 CentOS 上安装 FFmpeg 有几种方法,以下是两种常见的方法: ### 方法一:使用 RPM Fusion 仓库安装 1. **启用 RPM Fusion 仓库**: RPM Fusion 是一个第三方仓库,提供了许多 CentOS 官方仓库中没有的软件包。 ```bash…...
理解SQL中通配符的使用
前言 SQL 是一种标准化的结构化查询语言,涉及结构化查询时,高效地检索数据至关重要。而通配符是SQL中模式匹配的有效的方法。使用通配符可以更轻松地检索到所需的确切数据。通配符允许我们定义多功能查询条件。本文将 介绍SQL通配符的基础知识及用法。 …...

SpringBoot篇(简化操作的原理)
目录 一、代码位置 二、统一版本管理(parent) 三、提供 starter简化 Maven 配置 四、自动配置 Spring(引导类) 五、嵌入式 servlet 容器 一、代码位置 二、统一版本管理(parent) SpringBoot项目都会继…...

Cesium的模型(ModelVS)顶点着色器浅析
来自glTF和3D Tiles的模型会走ModelVS.glsl。这个文件不单独是把模型顶点转换为屏幕坐标,还包含了丰富的处理过程。 Cesium是根据定义的Define判断某个行为是否需要被执行,比如#define HAS_SILHOUETTE,说明需要计算模型外轮廓线。 Cesium的…...

机器人领域中的scaling law:通过复现斯坦福机器人UMI——探讨数据规模化定律(含UMI的复现关键)
前言 在24年10.26/10.27两天,我司七月在线举办的七月大模型机器人线下营时,我们带着大家一步步复现UMI,比如把杯子摆到杯盘上(其中1-2位学员朋友还亲自自身成功做到该任务) 此外,我还特地邀请了针对UMI做了改进工作的fastumi作者…...

C++之多态的深度剖析
目录 前言 1.多态的概念 2.多态的定义及实现 2.1多态的构成条件 2.1.1重要条件 2.1.2 虚函数 2.1.3 虚函数的重写/覆盖 2.1.4 选择题 2.1.5 虚函数其他知识 协变(了解) 析构函数的重写 override 和 final关键字 3. 重载,重写&…...
Microsoft Office PowerPoint制作科研论文用图
Microsoft Office PowerPoint制作科研论文用图 1. 获取高清图片2. 导入PPT3. 另存为“增强型windows元文件”emf格式4. 画图剪裁 1. 获取高清图片 这里指通过绘图软件画分辨率高的图片,我一般使用python画dpi600的图片。 2. 导入PPT 新建一个PPT(注意&a…...

go语言进阶之并发基础
并发 什么是并发,也就是我们常说的多线程,多个程序同时执行。 并发的基础 线程和进程 进程 进程是操作系统中一个重要的概念,指的是一个正在运行的程序的实例。它包含程序代码、当前活动的状态、变量、程序计数器和内存等资源。进程是系…...

po、dto、vo的使用场景
现在项目中有两类模型类:DTO数据传输对象、PO持久化对象,DTO用于接口层向业务层之间传输数据,PO用于业务层与持久层之间传输数据,有些项目还会设置VO对象,VO对象用在前端与接口层之间传输数据,如下图&#…...

聊一聊Elasticsearch的一些基本信息
一、Elasticsearch是什么 Elasticsearch简称ES,是一款分布式搜索引擎。它是在Apache Lucene基础之上采用Java语言开发的。 Elasticsearch的官方网站对它的解释是:Elasticsearch是一个分布式、RESTful的搜索和数据分析引擎。 通过上边的官方解释&#…...

Unity 两篇文章熟悉所有编辑器拓展关键类 (上)
本专栏基础资源来自唐老狮和siki学院,仅作学习交流使用,不作任何商业用途,吃水不忘打井人,谨遵教诲 编辑器扩展内容实在是太多太多了(本篇就有五千字) 所以分为两个篇章而且只用一些常用api举例,…...
Spring SPI、Solon SPI 有点儿像(Maven 与 Gradle)
一、什么是 SPI SPI 全名 Service Provider interface,翻译过来就是“服务提供接口”。基本效果是,申明一个接口,然后通过配置获取它的实现,进而实现动态扩展。 Java SPI 是 JDK 内置的一种动态加载扩展点的实现。 一般的业务代…...
合并排序算法(C语言版)
#include <stdio.h> void Copy(int *a, int *b, int left, int right) { int i; for(i0;i<right-left1;i) { a[ileft] b[i]; } } // 将 a[left,middle] 和 a[middle1,right]合并到 b[left, right]中 void Merge(int *a, int left, int midd…...
C++——输入一行文字,找出其中的大写字母、小写字母、空格数字以及其他字符各有多少。用指针或引用方法处理。
没注释的源代码 #include <iostream> using namespace std; int main() { char c; int ul0,ll0,sp0,di0,other0; cout<<"please input script c:"; while(cin.get(c)) { if(c\n) break; else if(c>A&&…...

【skywalking】maximum query complexity exceeded 3336 > 3000
问题 skywalking相关版本信息 jdk:17skywalking:10.1.0apache-skywalking-java-agent:9.3.0ElasticSearch : 8.8.2 问题描述 maximum query complexity exceeded 3336 > 3000 最大查询复杂度超过3336>3000 可能原因 查询条件过于复…...

开源一个开发的聊天应用与AI开发框架,集成 ChatGPT,支持私有部署的源码
大家好,我是一颗甜苞谷,今天分享一个开发的聊天应用与AI开发框架,集成 ChatGPT,支持私有部署的源码。 介绍 当前系统集成了ChatGPT的聊天应用,不仅提供了基本的即时通讯功能,还引入了先进的AI技术&#x…...

开发了一个成人学位英语助考微信小程序
微信小程序名称:石榴英语 全称:石榴英语真题助手 功能定位 北京成人学士学位英语辅助学习工具,包含记高频单词,高频词组,专项练习,模拟考试等功能。 开发背景 个人工作需要提高学习英文水平ÿ…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...