使用 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…...
开发了一个成人学位英语助考微信小程序
微信小程序名称:石榴英语 全称:石榴英语真题助手 功能定位 北京成人学士学位英语辅助学习工具,包含记高频单词,高频词组,专项练习,模拟考试等功能。 开发背景 个人工作需要提高学习英文水平ÿ…...
LeetCode16:最接近的三数之和
原题地址:. - 力扣(LeetCode) 题目描述 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。 返回这三个数的和。 假定每组输入只存在恰好一个解。 示例 1…...
VisualStudio2022配置2D图形库SFML
文章目录 1. 下载安装SFML库2. 创建C项目并配置SFML配置include目录和库目录链接SFML库配置动态链接库 3. 测试 1. 下载安装SFML库 SFML(Simple and Fast Multimedia Library)C库,适合2D游戏和图形界面,提供了以下模块࿱…...
「Mac畅玩鸿蒙与硬件4」鸿蒙开发环境配置篇4 - DevEco Studio 高效使用技巧
本篇将进一步介绍如何在 DevEco Studio 中高效使用各种功能,通过掌握快捷键、代码补全、调试工具等,帮助开发者在鸿蒙应用开发中大幅提升工作效率。 关键词 DevEco Studio快捷键代码补全调试工具项目导航 一、快捷键与高效操作 快捷键是提升开发效率的…...
构建生产级的 RAG 系统
对 RAG 应用程序进行原型设计很容易,但要使其高性能、健壮且可扩展到大型知识语料库却很困难。 本指南包含各种提示和技巧,以提高 RAG 工作流程的性能。我们首先概述一些通用技术 - 它们按照简单到复杂的顺序进行排列。然后,我们将更深入地研…...
完全透彻了解一个asp.net core MVC项目模板2
这是《完全透彻了解一个asp.net core MVC项目模板》的第二篇,如果你直接进入了本篇博文而不知道上下文,请先阅读《完全透彻了解一个asp.net core MVC项目模板》的第一篇。 文章目录 一、补充几个问题1、有关导航链接和Tag Helper2、_ViewStart.cshtml与…...
uniapp 如何调用音频
uniapp调用音频 button点击 <view><button click"startPlay">开始播放</button></view>方法实现 startPlay() { const innerAudioContext uni.createInnerAudioContext();innerAudioContext.src /static/sounds/oqc.mp3;innerAudioContex…...
在Facebook运营中使用住宅IP的重要性
在当前社交媒体的浪潮中,Facebook作为全球最大的社交网络之一,吸引了数以亿计的用户。为了在这一平台上实现有效的运营和推广,越来越多的博主和营销人员正在寻求最佳的养号策略。其中,IP地址的选择显得尤为重要,尤其是…...
EJB项目如何升级SpringCloud
记录某金融机构老项目重构升级为微服务过程1 如何从EJB架构拆分微服务 这个非常有趣的过程,整个过程耗时大致接近半年时光,需要考虑到重构升级保留原来的业务线,而且还要考虑后续的维护成本,保留现有的数据库表结构,…...
HTTPS 协议原理
一.HTTPS的定义 大家在刚开始学习的时候是不是也是非常好奇HTTP与HTTPS之间有什么区别和联系,两者都是应用层协议,而HTTPS是在HTTP的基础上引入了加密层,从而将HTTP的明文传输进行加密,保障数据的安全性 二.加密与解密 定义&#…...
Vxe UI 表格行编辑(默认不显示编辑框,点击后可编辑)
效果: HTML代码:(type"integer"为这个,是限制只能输入正整数或负整数,英文和汉字自动转成0) <vxe-tableshow-overflowkeep-sourcev-loading"loading":data"ruleList"ref"Table":row-config"{isHover: true}"height"…...
wordpress文章字号/女生学电子商务好吗
转自:http://bbs.9ria.com/thread-210322-1-1.html 首先,从copy开始说,简而言之,copy的目的就是生成一个新的实例,然后把其成员都按原实例赋值。对于非指针型的成员,比如BOOL, int, float,这样的…...
网站解析是做a记录吗/网上怎么发布广告
目录 1 BIO基本介绍 2 BIO工作机制 3 同步阻塞案例 4 BIO模式多发和多收消息 1 BIO基本介绍 BIO是就是传统的io编程,其相关的类和接口在Java.io BIO:同步阻塞,服务器实现模式为一个线程一个连接,即客户端有连接请求时服务器端就需要启动一个…...
清河网站建设电话/成人短期电脑培训班学费
InnovAntennas新品天线测试软件AnsysInnovAntennas已投资Ansys HFSS 2020 R2,这是一个完全基于3维网格的模拟软件包,可帮助完善和生产轻型Yagis的新型OWL和LFA Ultra Yagi。强度大,重量轻和低风载荷是现代业余无线电爱好者们对一款八木天线所…...
深圳独立站建站/58精准推广点击器
1、SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的。在SpringMVC 中定义一个Interceptor 非常简单,主要有两种方式,第一种方式是要定义的Interceptor类要实现了Spring 的HandlerInterceptor 接口,或者是这个类继承实现…...
discuz 做视频网站/网络营销的核心是
2019独角兽企业重金招聘Python工程师标准>>> Kubernetes社区的生态繁荣和该领域技术的快速茁壮发展,已经是众所周知。Kubernetes领域有太多强大的、创新的技术产品,而最近引起我注意的项目是ExternalDNS。这是在近期的POC期间客户主动咨询起来…...
淘宝客网站空间/百度关键词收录排名
JavaScript实现的一个动态查询表格,随着文本框中资料的改变,下边Table中的资料会自动筛选。 <% page language"java" contentType"text/html; charsetUTF-8" pageEncoding"UTF-8"%> <!DOCTYPE <html> <…...