使用 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-Typemultipart/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>

注意:

  1. 使用 FormData 上传文件切片,确保文件部分是以二进制格式上传的。
  2. 设置 Content-Typemultipart/form-data

服务端合并切片

实现原理

1. 搭建服务

  • 服务搭建:引入 express 模块,创建了一个 express 应用实例 app

  • 设置端口号 PORT 并使用 app.listen() 启动 express 应用,使其监听指定的端口。

2. 接受并存储切片

  • 接收切片:服务端定义了一个 /upload 路由,使用 multer 中间件处理上传的文件切片。multer 会将上传的文件暂存到指定的目录(例如 uploads/)。

  • 保存切片:服务端根据 filenamechunkIndex 创建一个临时目录,并将上传的切片移动到该目录中。例如,切片路径可能为 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}`);
});

注意:

  1. fs 模块的方法转换为 Promise 版本,以便防止文件合并顺序错误而导致文件损坏。
  2. 在创建输出文件流时,设置 flags: 'w'encoding: null,确保以二进制格式写入文件。
  3. 在创建输入文件流时,设置 encoding: null,确保以二进制格式读取文件。

总结

前端:
点击按钮选取文件后,通过事件对象 event 拿到文件并按指定大小(如 1MB)进行分片,使用循环遍历每个分片,创建 blob 对象表示分片,将分片及其相关信息(文件名、分片索引、总分片数)封装到 FormData 对象中,最后使用 axios 发送 POST 请求上传每个分片。

服务端:
服务端通过 API 接口(如 /upload)接收前端上传的每个分片,解析请求中的 formData,提取分片数据、文件名、分片索引和总分片数,使用 expressmulter 接收这些片段,将其保存到临时目录,并在接收到最后一个片段时调用 mergeChunks 函数将所有片段合并成一个完整的文件。合并完成后,删除临时文件。整个过程包括文件切片、上传、保存、合并和清理,确保了大文件的高效传输和处理。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV视觉分析之目标跟踪(6)轻量级目标跟踪器类TrackerNano的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 Nano 跟踪器是一个超轻量级的基于深度神经网络&#xff08;DNN&#xff09;的通用目标跟踪器。 由于特殊的模型结构&#xff0c;Nano 跟踪器速度…

C数组手动输入问题

问题界面 解析 输入数组数据也需要加取地址符吗&#xff1f;数组不就是地址了吗&#xff1f; 理解array[i]和array[i][j]的区别&#xff1a; array[i]是一个指向第i行第一个元素的指针&#xff08;int*类型&#xff0c;指向array[i][0]&#xff09;。 array[i][j]是一个int类…

Hadoop-002-部署并配置HDFS集群

集群规划 Hadoop HDFS的角色包含 NameNode(主节点管理者)、DataNode(从节点工作者)、SeconddaryNameNode(从节点辅助) 节点CPU内存hadoop-11C4Ghadoop-21C2Ghadoop-31C2G 一、下载上传Hadoop包 注意: 登录hadoop-1节点root用户执行 1、官网下载安装包后上传 到hadoop-1服务…

Android 在github网站下载项目:各种很慢怎么办?比如gradle下载慢;访问github慢;依赖下载慢

目录 访问github慢gradle下载慢依赖下载慢 前言 大家好&#xff0c;我是前期后期&#xff0c;在网上冲浪的一名程序员。 为什么要看这篇文章呢&#xff1f;问题是什么&#xff1f; 我们在Github上面看到一些好的项目的时候&#xff0c;想下载下来研究学习一下。但经常遇到各…

信息安全数学基础(35)同态和同构

一、同态 定义&#xff1a; 设(M,)和(S,)是两个群&#xff08;或更一般的代数系统&#xff09;&#xff0c;如果存在一个映射σ:M→S&#xff0c;使得对于M中的任意两个元素a、b&#xff0c;都有σ(ab)σ(a)σ(b)&#xff0c;则称σ为M到S的同态或群映射。 性质&#xff1a; 同…

微信小程序中点击搜素按钮没有反应,可能是样式问题(按钮被其他元素覆盖或遮挡)

文章目录 1. 确认 bindtap 绑定在正确的元素上2. 检查是否有遮挡或重叠元素3. 检查 this 上下文绑定问题4. 清除微信小程序开发者工具的缓存5. 用微信开发者工具查看事件绑定6. 确保 handleSearch 没有拼写错误进一步调试 1、searchResults.wxml2、searchResults.wxss3、search…

实验干货|电流型霍尔传感器采样设计03-信号调理

在前两篇博客中&#xff0c;将霍尔输出的电流信号转换成了有正有负的电压信号&#xff0c;但是DSP需要采集0~3V的电压信号&#xff0c;因此需要对信号缩放并抬升至全部为正的信号。 常见的方法是&#xff0c;通过比例放大(缩小)电路对信号进行放缩&#xff0c;通过加法电路抬升…

SQLI LABS | Less-20 POST-Cookie Injections-Uagent field-error based

关注这个靶场的其它相关笔记&#xff1a;SQLI LABS —— 靶场笔记合集-CSDN博客 0x01&#xff1a;过关流程 输入下面的链接进入靶场&#xff08;如果你的地址和我不一样&#xff0c;按照你本地的环境来&#xff09;&#xff1a; http://localhost/sqli-labs/Less-20/ 可以看到…

爬虫+数据保存2

爬取数据保存到MySQL数据库 这篇文章, 我们来讲解如何将我们爬虫爬取到的数据, 进行保存, 而且是把数据保存到MySQL数据库的方式去保存。 目录 1.使用pymysql连接数据库并执行插入数据sql代码(insert) 2.优化pymysql数据库连接以及插入功能代码 3.爬取双色球网站的数据并保…

echarts 遍历多个图表,并添加resize缩放

数据结构&#xff1a; data() { return { charts: [ { title: Chart 1, xAxisData: [Mon, Tue, Wed, Thu, Fri, Sat, Sun], yAxisData: [120, 200, 150, 80, 70, 110, 130], }, { title: Chart 2, xAxisData: [Jan, Feb, Mar, Apr, May, Jun, Jul], yAxisData: [22…

Linux 中,flock 对文件加锁

在Linux中&#xff0c;flock是一个用于对文件加锁的实用程序&#xff0c;它可以帮助协调多个进程对同一个文件的访问&#xff0c;避免出现数据不一致或冲突等问题。以下是对flock的详细介绍&#xff1a; 基本原理 flock通过在文件上设置锁来控制多个进程对该文件的并发访问。…

(五)Web前端开发进阶2——AJAX

目录 2.Axios库 3.认识URL 4.Axios常用请求方法 5.HTTP协议——请求报文/响应报文 6.前后端分离开发 7.Element组件库 1.Ajax概述 AJAX 是异步的 JavaScript和XML(Asynchronous JavaScript And XML)。简单点说&#xff0c;就是使用XMLHttpRequest 对象与服务器通信。它可…

使用C#学习Office文件的处理(pptx docx xlsx)

Office文件 是指PPT 、word、Excel 这些常用工具生成的文件 &#xff0c;例如 pptx docx xlsx。 这些文件的读取和生成有很多很多库 例如 NOPI 、DevExpress、C1、Aspose、Teleric 等等&#xff0c;各有各的优缺点。俺今天不讲这个&#xff0c;俺只是讲讲如何了解Office文件的…

2020年下半年网络规划设计师上午真题及答案解析

1.在支持多线程的操作系统中&#xff0c;假设进程P创建了线程T1&#xff0c;T2&#xff0c;T3&#xff0c;那么下列说法中正确的是&#xff08; &#xff09;。 A.该进程中已打开的文件是不能被T1&#xff0c;T2和T3共享的 B.该进程中T1的栈指针是不能被T2共享&#xff0c;但…

Java 使用Maven Surefire插件批量运行单元测试

在基于Maven的Java项目中可以使用Maven 的 mvn test 命令来运行单元测试。 示例 有一个简单的Maven 项目&#xff0c; pom.xml 只导入了JUnit 5 的相关依赖&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://m…

Linux CentOS7下创建SFTP服务器

本文详细介绍了在Linux CentOS上部署安全文件传输协议&#xff08;SFTP&#xff09;服务器的全过程。SFTP基于SSH&#xff08;安全壳层协议&#xff09;提供文件传输服务&#xff0c;继承了SSH的安全特性&#xff0c;如数据加密、完整性验证和服务器认证等&#xff0c;确保数据…

电科金仓(人大金仓)更新授权文件(致命错误: XX000: License file expired.)

问题:电科金仓(人大金仓)数据库链接异常,重启失败,查看日志如下: 致命错误: XX000: License file expired. 位置: PostmasterMain, postmaster.c:725 解决方法: 一、下载授权文件 根据安装版本在官网下载授权文件(电科金仓-成为世界卓越的数据库产品与服务提供商)…

3D Gaussian Splatting代码详解(二):模型构建

3 模型构建 gaussians GaussianModel(dataset.sh_degree) 3.1 初始化函数 __init__ 构造函数 构造函数 __init__ 的主要作用是初始化 3D 高斯模型的各项参数和激活函数&#xff0c;用于生成 3D 空间中的高斯表示。 初始化球谐函数的参数&#xff1a; self.active_sh_degre…

一文讲明白大模型分布式逻辑(从GPU通信原语到Megatron、Deepspeed)

1. 背景介绍 如果你拿到了两台8卡A100的机器&#xff08;做梦&#xff09;&#xff0c;你的导师让你学习部署并且训练不同尺寸的大模型&#xff0c;并且写一个说明文档。你意识到&#xff0c;你最需要学习的就是关于分布式训练的知识&#xff0c;因为你可是第一次接触这么多卡…

Python毕业设计选题:基于协同过滤的校园音乐推荐系统小程序-django+uniapp

开发语言&#xff1a;Python框架&#xff1a;djangouniappPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录界面 管理员功能界面 用户管理 音乐信息管理 音乐类型管理 留言板管…