Vue3 + Nodejs 实战 ,文件上传项目--实现文件批量上传(显示实时上传进度)
目录
技术栈
1.后端接口实现
2.前端实现
2.1 实现静态结构
2.2 整合上传文件的数据
2.3 实现一键上传文件
2.4 取消上传
博客主页:専心_前端,javascript,mysql-CSDN博客
系列专栏:vue3+nodejs 实战--文件上传
前端代码仓库:jiangjunjie666/my-upload: vue3+nodejs 上传文件的项目,用于学习 (github.com)
后端代码仓库:jiangjunjie666/my-upload-server: nodejs上传文件的后端 (github.com)
欢迎关注
在上一篇中,我们创建好了前端Vue3,后端nodejs的项目,并且实现了一个图片上传的功能,地址在: Vue3 + Nodejs 实战 ,文件上传项目--实现图片上传-CSDN博客 ,该篇实现了文件的批量上传并且显示实时的上传进度。
技术栈
前端:Vue3 Vue-router axios element-plus...
后端:nodejs express...
1.后端接口实现
我们先把后端上传文件的接口写好,后端接收文件我用的并不是原生的js,用的是:formidable,所以没看过上一篇创建项目的一定要去看看喔。Vue3 + Nodejs 实战 ,文件上传项目--实现图片上传-CSDN博客
在路由文件中新增一个接口
//上传文件
router.post('/fileUpload', handler.fileUp)
在处理函数文件中编写接口函数
其实后端这次的代码和上一篇中的上传图片代码相差不大,无非就是进行一些文件大小控制,返回相应等,不过我新增了个保留原始的文件名的方法,用的是fs模块中的重命名方法。
其中上传的路径放在了public下的file文件夹中了,这个你们可以根据自己的喜好进行更改。
exports.fileUp = (req, res, next) => {//上传大小小于5Mb的文件//接收数据const form = formidable({multiples: true,uploadDir: path.join(__dirname, '../../public/file'),keepExtensions: true})form.parse(req, (err, fields, files) => {if (err) {next(err)return}//限制上传文件的大小if (files.file.size > 1024 * 1024 * 5) {//删除对应的文件const folderPath = path.join(__dirname, '../../public/file/' + files.file.newFilename) // 文件路径fs.unlinkSync(folderPath)res.send({code: 400,msg: '上传文件过大'})return}//修改保存文件的默认nameconst folderPath = path.join(__dirname, '../../public/file/' + files.file.newFilename) // 文件路径let newName = path.join(__dirname, '../../public/file/' + files.file.originalFilename)//对读取的文件进行重命名console.log(newName)fs.rename(folderPath, newName, (err) => {if (err) {console.log(err)return} else {console.log('重命名成功')res.send({code: 200,msg: '上传成功'})}})})
}
这就是接口函数了
上一篇中漏讲了一个地方,就是路由要在app.js中进行注册,否则该接口是调用不了的,不过有nodejs基础的应该都能想到这个问题了。
这样注册以后接口就可以正常访问了,不过注意我这里接口带了/api,所以在前端调用时记得带上/api。
2.前端实现
2.1 实现静态结构
我想到的是这样一种效果,可以显示文件名,文件的大小,文件的上传状态等等信息
不过文件的状态这里我做了三种显示效果,是 准备上传--> 上传进度条 --> 上传成功(上传失败),table表格用的是Element-plus的 el-table组件,这里的进度我也没有自己写了,用的是Element-plus的组件,不过你们想自己实现的话也很简单(如果想的话可以自己试试)。
注意这里的input选择文件框是隐藏的,点击按钮时触发他的点击事件就行。
注意这里的input选择框默认是只支持选择单文件的,要想实现多文件选择要加上multiple属性
<template><div class="container"><input type="file" ref="fileInputRef" style="display: none" @change="handleFileClick" multiple /><p>不可选中重复的文件</p><el-button type="primary" @click="handleBtnClick">可选择多个文件</el-button><el-table :data="tableData" style="width: 100%"><el-table-column prop="name" label="文件名" width="400" /><el-table-column prop="size" label="文件大小" width="400" /><!-- 控制显示 --><el-table-column label="文件状态" width="400"><template #default="scope1"><span v-if="scope1.row.status == '准备上传'">{{ scope1.row.status }}</span><el-progress :percentage="percentage" stroke-width="8" :width="100" :duration="1" v-if="scope1.row.status == '正在上传'" /><span v-if="scope1.row.status == '上传成功'" style="color: #67c23a">{{ scope1.row.status }}</span><span v-if="scope1.row.status == '上传失败'" style="color: red">{{ scope1.row.status }}</span></template></el-table-column><el-table-column prop="address" label="操作"><template #default="scope3"><el-button size="small" type="danger" @click="handleDelete(scope3.$index, scope3.row)" :disabled="scope3.row.status == '正在上传'">删除</el-button></template></el-table-column></el-table><el-button type="success" style="margin-top: 20px" @click="handleUpload" :disabled="tableData.length == 0">一键上传</el-button><el-button size="default" style="margin-top: 20px" type="danger" @click="cancelUpload">取消上传</el-button><el-button size="default" style="margin-top: 20px" type="danger" @click="free">开启上传</el-button></div>
</template><style lang="scss" scoped>
p {font-size: 14px;color: red;margin: 10px 0;
}
</style>
2.2 整合上传文件的数据
通过基本的静态结构,可以看到table中需要一个tableDatao'n的数据,所以hai'yao我的实现思路是选择上传的文件后将需要用到的数据整合到tableData中供table表格展示,然后还要将上传文件需要用到的formData放在一个数组中保存,在之后上传时再用里面的数据。
定义好需要用到的数据
import { ref } from 'vue'
//存放文件的数组
const fileList = ref([])
//存放table的数据
const tableData = ref([])
//input框的ref
const fileInputRef = ref(null)
选择文件后整合数据
//触发文件选择事件
const handleBtnClick = () => {fileInputRef.value.click()
}//触发文件选择框
const handleFileClick = (e) => {//遍历选中的所有文件添加到数组中for (let i = 0; i < e.target.files.length; i++) {let selectedFile = e.target.files[i]//将数据整合起来放进数组中tableData.value.push({id: tableData.value.length,name: selectedFile.name,//判断文件大小,大于0.1mb使用mb,否则使用kbsize: selectedFile.size > 1024 * 1024 ? (selectedFile.size / 1024 / 1024).toFixed(2) + 'mb' : (selectedFile.size / 1024).toFixed(2) + 'kb',status: '准备上传'})fileList.value.push(selectedFile)}
}
删除文件的函数
//删除选中的文件
const handleDelete = (index, row) => {console.log(index)//根据index和id进行对比,删除其中的元素tableData.value.splice(index, 1)fileList.value.splice(index, 1)
}
2.3 实现一键上传文件
因为我给table中的数据加了status的文件上传的状态,所以我在点击一键上传后,主要使用该字段来进行判断该上传第几个文件,这里会定义一个上传文件的index索引,然后采用递归的方式循环上传所有的文件,其实一键上传也是一个一个文件的上传。
定义好需要用到的数据
//正在上传的文件的index
const fileIndex = ref(0)//一键上传文件
const handleUpload = () => {fileIndex.value = 0//先遍历所有的循环找到没有上传的文件的indextableData.value.forEach((item) => {if (item.status == '上传成功' || item.status == '上传失败') fileIndex.value++})if (tableData.value.length == fileIndex.value) {return}tableData.value[fileIndex.value].status = '正在上传'//调用上传文件的函数uploadFile(fileList.value[fileIndex.value]).then((res) => {if (res) {tableData.value[fileIndex.value].status = '上传成功'//重新调用函数let timer = setTimeout(() => {//进度条归0percentage.value = 0handleUpload()clearTimeout(timer)}, 1000)} else {//返回的是一个promisetableData.value[fileIndex.value].status = '上传失败'}})
}
上传文件的函数
因为这里需要做一个实时的上传文件的进度条显示,然后Element-plus的进度条组件中绑定了一个元素来控制其进度条显示,这里需要获取到真实的上传进度,因为我用的是axios封装的请求,所以可以使用其中的一个回调来获取进度,不过原生的axaj也是可以获取的,这个根据自己的项目来
//进度条
const percentage = ref(0)
//导入axios构造的函数
import { http} from '@/api/http.js'
//上传文件的函数
const uploadFile = async (value) => {// 创建一个FormData对象来包装文件const formData = new FormData()formData.append('file', value)try {const res = await http.post('/api/fileUpload', formData, {headers: {'Content-Type': 'multipart/form-data'},//监听进度onUploadProgress: (progressEvent) => {//进度条const loaded = progressEvent.loadedconst total = progressEvent.totalconst percentCompleted = Math.round((loaded * 100) / total)//在这里改变进度条的值percentage.value = percentCompletedconsole.log(`上传进度: ${percentCompleted}%`)}})// 等待 Promise 解析console.log(res)// 根据 Promise 解析的结果来判断上传是否成功if (res.code === 200) {ElMessage({type: 'success',message: '上传成功'})return true} else {ElMessage({type: 'error',message: res.msg})return false}} catch (error) {return false}
}
现在一键上传文件已经完成了,并且可以实时的显示上传的进度,我们现在可以测试一下。
为了方便的看到上传进度,我们可以将浏览器的网络调低一点(不过这样做了就要把请求拦截器中的超时设置的长一点),设置个30s应该差不多
选择好文件后就可以点击上传了
可以看到上传进度是实时显示的 ,并且从后端中文件夹的图片传输进度也能看出来
2.4 取消上传
在axios中要想取消上传(取消请求),需要用到cancel token,详情可以查看这个axios中文文档|axios中文网 | axios
在请求拦截器中定义一个token并导出在组件中使用
//axios请求拦截器
import axios from 'axios'
import { ElMessage } from 'element-plus'
const http = axios.create({baseURL: 'http://127.0.0.1:3000',timeout: 30000
})// 创建一个 Cancel Token 对象
const cancelSource = axios.CancelToken.source()
//请求拦截器
http.interceptors.request.use((config) => {config.cancelToken = cancelSource.tokenreturn config
})//返回拦截器
http.interceptors.response.use((response) => {return response.data},//失败回调(error) => {ElMessage({type: 'error',message: error.message})return Promise.reject(error)}
)export { http, cancelSource }
组件中点击取消按钮触发该函数,不过之前要导入cancelSource
import { http, cancelSource } from '@/api/http.js'
//取消发送
const cancelUpload = () => {console.log('取消发送')// 取消上传cancelSource.cancel('请求取消')
}
不过我这样写会出现一个小bug,就是如果取消上传后就不能再次上传了,因为我的axios发送的post用的都是同一个axios构造出来的实例,关闭后其他的的请求也用不了了,就无法再次上传,解决方法很简单就是直接刷新浏览器即可,或者在发送请求时每次都用axios重新构造一个实例,这样就不会互相受到影响了,不过我偷懒了一波就直接采用了刷新浏览器的方式了,你们如果要做的话可以优化一下这个功能。
const free = () => {//这里我将开启上传设置成了刷新页面,但实际情况下,可以重新创建axios请求示例,其他的请求就不会受到影响了,有兴趣的可以自己实现一下该功能location.reload()
}
到此该一键上传文件并且实时显示进度条的功能就实现了。
下一篇是完成大文件的切片上传,敬请关注等候。
相关文章:
Vue3 + Nodejs 实战 ,文件上传项目--实现文件批量上传(显示实时上传进度)
目录 技术栈 1.后端接口实现 2.前端实现 2.1 实现静态结构 2.2 整合上传文件的数据 2.3 实现一键上传文件 2.4 取消上传 博客主页:専心_前端,javascript,mysql-CSDN博客 系列专栏:vue3nodejs 实战--文件上传 前端代码仓库:jiangjunjie…...
狂砸40亿美元,亚马逊向OpenAI竞争对手Anthropic投资
9 月 25 日下午,亚马逊在公司官网发布,向大模型公司 Anthropic 投资 40 亿美元, Anthropic以拥有对标 ChatGPT 的谈天机器人 Claude 而出名。 这项新的战略合作将结合双方在更安全的生成式AI领域的技术和专业知识,加速Anthropic未…...
目标检测YOLO实战应用案例100讲-基于YOLOv5_tiny算法的路面裂缝智能检测
目录 前言 国内外研究现状 公路路面裂缝检测方法现状 基于深度学习检测算法现状...
P5682 [CSP-J2019 江西] 次大值% 运算 set 去重的一道好题
#include <bits/stdc.h> using namespace std; int n, x, len, a[100010], ans; set<int> s; set<int>::iterator asd; int main() {/*a[n-1] 是最大的a[n-2] 可能是次大的a[n]%a[n-1]<a[n-1] 不可能是最大的,可能是次大的a[n-1]%a[n-2]<…...
vue3后台管理框架之API接口统一管理
在开发项目的时候,接口可能很多需要统一管理。在src目录下去创建api文件夹去统一管理项目的接口; 参数请参考mock中的模拟接口 //统一管理咱们项目用户相关的接口import request from @/utils/requestimport type { loginForm, loginResponseData, userInfoReponseData } fro…...
线性表的插入、删除和查询操作
线性表的插入、删除和查询操作 1、定义线性表 定义一个线性结构,有列表默认长度设置为50,列表数量 #include <stdio.h> #define MaxSize 50typedef int Element; typedef struct{Element data[MaxSize];int length; }SqList;2、顺序表插入 插入…...
基于深度学习网络的疲劳驾驶检测算法matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1疲劳检测理论概述 4.2 本课题说明 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 In_layer_Size [227 227 3]; img_size [224,…...
【文件系统】Linux文件系统的基本存储机制
Linux文件系统是Linux操作系统的重要组成部分,它负责管理计算机存储设备上的文件和目录。Linux文件系统采用类Unix的设计,具有强大的性能和可扩展性,支持多种文件系统类型,如ext4、XFS、Btrfs等。在项目存储架构的设计中ÿ…...
Outlook导入导出功能灰色,怎么解决
下载安装 Outlook 软件后,登陆账号,然后选择“文件” - “导出”,结果发现“导出”按钮是灰色的,根本无法导出。根据官方说法:由于配置没有完成或者office产品没有正确激活。outlook导出键为灰色原因由于配置没有完成或…...
Chrome 同站策略(SameSite)问题
问题产生 问题复现: A项目页面使用 iframe 引用了B项目 B项目登录页面输入账号密码后点击登录 无法跳转 尝试解决: 在B项目修改了跳转方式 但无论是 this.$router.push 还是 window.herf 都无法实现跳转在iframe中使用 sandbox 沙箱属性 同样无法实现跳…...
docker搭建nginx+php-fpm
docker run --name nginx -p 8898:80 -d nginx:1.20.2-alpine# 将容器nginx.conf文件复制到宿主机 docker cp nginx:/etc/nginx/nginx.conf /usr/local/nginx/conf/nginx.conf# 将容器conf.d文件夹下内容复制到宿主机 docker cp nginx:/etc/nginx/conf.d /usr/local/nginx/conf…...
数据结构与算法---单调栈结构
数据结构与算法---单调栈结构 1 滑动窗口问题 1 滑动窗口问题 1 滑动窗口问题 由一个代表题目,引出一种结构 【题目】 有一个整型数组 arr 和一个大小为 w 的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置。 例如,数组为[4,3,…...
Python爬虫:某书平台的Authorization参数js逆向
⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ 🐴作者:秋无之地 🐴简介:CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作,主要擅长领域有:爬虫、后端、大数据开发、数据分析等。 🐴欢迎小伙伴们点赞👍🏻、收藏⭐️、…...
Android MediaCodec 框架 基于codec2
系列文章的目的是什么? 粗略: 解码需要哪些基础的服务?标准解码的调用流程?各个流程的作用是什么?解码框架的层次?各个层次的作用? 细化: 解码参数的配置?解码输入数…...
【RocketMQ 系列三】RocketMQ集群搭建(2m-2s-sync)
您好,我是码农飞哥(wei158556),感谢您阅读本文,欢迎一键三连哦。 💪🏻 1. Python基础专栏,基础知识一网打尽,9.9元买不了吃亏,买不了上当。 Python从入门到精…...
Go TLS服务端绑定证书的几种方式
随着互联网的发展,网站提供的服务类型和规模不断扩大,同时也对Web服务的安全性提出了更高的要求。TLS(Transport Layer Security)[1]已然成为Web服务最重要的安全基础设施之一。默认情况下,一个TLS服务器通常只绑定一个证书[2],但…...
【算法与数据结构】--高级算法和数据结构--排序和搜索
一、常见排序算法 以下是一些常见的排序算法,包括冒泡排序、选择排序、插入排序、快速排序和归并排序。每种排序算法的讲解以及附带C#和Java示例: 1.1 冒泡排序 (Bubble Sort) 讲解: 冒泡排序是一种简单的比较排序算法。它多次遍历待排序的…...
【Java】jvm 元空间、常量池(了解)
JDK1.8 以前的 HotSpot JVM 有方法区,也叫永久代(permanent generation)方法区用于存放已被虚拟机加载的类信息,常量、静态遍历,即编译器编译后的代码JDK1.7 开始了方法区的部分移除:符号引用(S…...
Spring Boot自动加载
问:自动装配如何实现的? 答:简单来说就是自动去把第三方组件的Bean装载到IOC容器中,不需要开发人员再去写Bean相关的配置,在springboot应用里面只需要在启动类上去加上SpringBootApplication注解,就可以去实…...
MPNN 模型:GNN 传递规则的实现
首先,假如我们定义一个极简的传递规则 A是邻接矩阵,X是特征矩阵, 其物理意义就是 通过矩阵乘法操作,批量把图中的相邻节点汇聚到当前节点。 但是由于A的对角线都是 0.因此自身的节点特征会被过滤掉。 图神经网络的核心是 吸周围…...
Flink kafka 数据汇不指定分区器导致的问题
背景 在flink中,我们经常使用kafka作为flink的数据汇,也就是目标数据的存储地,然而当我们使用FlinkKafkaProducer作为数据汇连接器时,我们需要注意一些注意事项,本文就来记录一下 使用kafka数据汇连接器 首先我们看…...
【软考】14.1 面向对象基本概念/分析设计测试
《面向对象开发》 对象 现实生活中实际存在的一个实体;构成系统的一个基本单位由对象名、属性和方法组成 类 实体的形式化描述;对象是类的实例,类是对象的模板可分为:实体类:现实世界中真实的实体接口类(边…...
MFC-对话框
目录 1、模态和非模态对话框: (1)、对话框的创建 (2)、更改默认的对话框名称 (3)、创建模态对话框 1)、创建按钮跳转的界面 2)、在跳转的窗口添加类 3࿰…...
Essential Steps in Natural Language Processing (NLP)
💗💗💗欢迎来到我的博客,你将找到有关如何使用技术解决问题的文章,也会找到某个技术的学习路线。无论你是何种职业,我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章,也欢…...
Flink中KeyBy、分区、分组的正确理解
1.Flink中的KeyBy 在Flink中,KeyBy作为我们常用的一个聚合类型算子,它可以按照相同的Key对数据进行重新分区,分区之后分配到对应的子任务当中去。 源码解析 keyBy 得到的结果将不再是 DataStream,而是会将 DataStream 转换为 Key…...
QT6集成CEF3--01 准备工作
QT6集成CEF3--01 准备工作 一、所有使用到的工具软件清单:二、准备工作三、cefclient示例程序四、特别注意 一、所有使用到的工具软件清单: CEF 二进制发行包 cef_binary_117.2.5gda4c36achromium-117.0.5938.152_windows64.tar.bz2 CMake 编译工具 cmake-3.22.6-windows-x86_…...
随机误差理论与测量
文章目录 第1节 随机误差的性质和特点第2节 随机误差的数字特性标准差的估计 第3节 单次测量结果的精度指标第4节 多次测量结果的精度指标算数平均值的分布特性与标准差算数平均值的置信度算数平均值的精度指标(常用的有4个) 第5节 非等精度测量 第1节 随机误差的性…...
树莓派4b配置通过smbus2使用LCD灯
出现报错: FileNotFoundError: [Errno 2] No such file or directory: ‘/dev/i2c-1’ 则说明没有打开I2C,可通过如下步骤进行设置 1、打开树莓派配置 sudo raspi-config2、进入Interface Options,配置I2C允许 目前很多python3版本已经不…...
UPS 原理和故障案例分享
摘要:不间断电源UPS (Uninterruptible Power System),主要是由整流器、 逆变器、静态旁路和储能装置等组成;具备高可靠性、高可用性和高质量的独立 电源。通过对收集的 UPS 故障案例进行分析,从施工,调试和运行三个方面筛选 出四个故障案例与…...
Stream流中的 max()和 sorted()方法
需求:某个公司的开发部门,分为开发 一部 和 二部 ,现在需要进行年中数据结算。分析: 员工信息至少包含了(名称、性别、工资、奖金、处罚记录)开发一部有 4 个员工、开发二部有 5 名员工分别筛选出 2 个部门…...
常德做网站建设的公司/东莞seo整站优化火速
第一章 Java开发环境的搭建 常用的DOS命令 1、怎么打开DOS命令窗口 win键 r (组合键):可以打开“运行”窗口 在运行窗口文本框中输入: cmd 然后回车 2、什么是DOS命令呢? 在DOS命令窗口中才可以输入并执行DOS命令。 在最…...
网站建设 自助建站/最近几天的新闻大事
关闭防火墙: 控制面板-〉防火墙-〉不启用防火墙-〉高级设置-〉域防火墙设置-〉关闭防火墙转载于:https://www.cnblogs.com/promise-7/archive/2013/04/26/3044224.html...
织梦和wordpress/网站设计方案模板
渐变Gradients-径向渐变 -颜色结点 微信小程序交流群:111733917 | 微信小程序从0基础到就业的课程:https://edu.csdn.net/topic/huangjuhua 通用语法 径向渐变由它的中心定义。 为了创建一个径向渐变,你也必须至少定义两种颜色结点。颜色结点…...
移动端网站的优势/友情视频
这是思科出的最新的Packet Tracer 6.1 student完整版,讲师版就是多了出题的模块,在51cto的下载频道也能找到,我是懒得去分割上传了,直接从百度盘下吧。Packet Tracer 6.1版本最明显的区别是加了防火墙ASA5505,同时也支持HSRP&…...
培训做网站/网站管理工具
用c#操作Mongodb(附demo) 因为需要,写了一个基于泛型的helper,这样要使用起来方便一点。 为了大家也不重复造轮子,所以发出来希望能帮到谁。 复杂的查询最好用linq,这也是mongodb官方建议的。 mongodb的C#配置 这部分很多文章都提…...
来年做哪些网站致富/如何注册属于自己的网站
ActiveMQ发送消息 转载:http://www.cnblogs.com/yangchongxing/p/9042401.html Java消息服务(Java Message Service, JMS)是一个Java标准,定义了使用消息代理的通用API。ActiveMQ是一个开源消息代理产品,也是使用JMS进行异步消息传递的最佳选…...