express+mysql+vue,从零搭建一个商城管理系统7--文件上传,大文件分片上传
提示:学习express,搭建管理系统
文章目录
- 前言
- 一、安装multer,fs-extra
- 二、新建config/upload.js
- 三、新建routes/upload.js
- 四、修改routes下的index.js
- 五、修改index.js
- 六、新建上传文件test.html
- 七、开启jwt验证token,通过login接口生成token
- 总结
前言
需求:主要学习express,所以先写service部分
一、安装multer,fs-extra
npm install multer --save
npm install fs-extra --save
二、新建config/upload.js
upload.js
const fs = require('fs');
const fsExtra = require('fs-extra');
const path = require('path');
const rootDir = path.resolve(__dirname,'../upload/');
const temporaryDir = path.resolve(__dirname,'../upload/temporary/');
const errFun = (msg,code)=>{return {code:code||500,success:false,msg:msg||'操作失败'}
}
const sucFun = (data,msg)=>{return {code:200,success:true,msg:msg||'操作成功',data,}
}
const uploadUtil = {upload:(req)=>{const { fileMD5,chunkMD5 } = req.body;return new Promise ((resolve,reject)=>{const folderPath = temporaryDir+'/'+fileMD5;const filePath = temporaryDir+'/'+fileMD5+'/'+chunkMD5if(!fs.existsSync(folderPath))fs.mkdirSync(folderPath);fs.writeFile(filePath,req.file.buffer,(err)=>{if(err)reject(errFun('切片上传失败'));resolve(sucFun({},'上传成功'));})});},merge:async(req)=>{const {fileMD5,chunkMD5Arr,type} = req.body;const folderPath = temporaryDir+'/'+fileMD5;let chunkBufferArr = [];for(let i=0;i<chunkMD5Arr.length;i++){let chunkBuffer = await fs.readFileSync(folderPath+'/'+chunkMD5Arr[i]);chunkBufferArr.push(chunkBuffer);}console.log(chunkBufferArr)let fileurl = rootDir+'/images/'+fileMD5+'-'+(new Date().getTime())+'.'+type;fs.writeFile(fileurl,Buffer.concat(chunkBufferArr),(err)=>{if(err)errFun(fileMD5+'文件切片merge失败');sucFun({url:fileurl},'文件切片merge成功');//删除临时文件夹以及文件夹下的所有文件fsExtra.removeSync(folderPath);});}
}
module.exports = uploadUtil;
三、新建routes/upload.js
const uploadUtil = require('../config/upload');
//文件流的key与multer的single方法的参数param,必须保持一致,不一致接收不到文件流//fd.append('xxx',chunk);//multer().single('xxxx');const multer = require('multer');
const chunk = multer().single('chunk');const uploadRoutes = (router)=>{router.post('/upload/upload',chunk,async (req,res)=>{const result = await uploadUtil.upload(req);res.json(result);});router.post('/upload/merge',async (req,res)=>{const result = await uploadUtil.merge(req);res.json(result);});
}
module.exports = uploadRoutes;
四、修改routes下的index.js
const userRoutes = require('./user');
const uploadRoutes = require('./upload');
const routes = (router)=>{//user路由userRoutes(router);//upload路由uploadRoutes(router);
}module.exports = routes;
五、修改index.js
注释jwt的token验证,方便测试。如果打开验证,需要通过login接口生成token,上传文件request请求头部携带
const express = require('express');
const app = express();
const router = express.Router();
const jwt = require('./config/jwt');const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());const port = 1990;app.all('*', (req, res, next) => {res.header('Access-Control-Allow-Origin', '*');res.header("Access-Control-Allow-Headers", "Authorization,token,content-type");res.header('Access-Control-Allow-Methods', '*');res.header('Content-Type', 'application/json;charset=utf-8');next();
});//全局验证token
// app.use('/*',(req,res,next)=>{
// let notValidateData = ['/user/login','/user/register'];
// if(notValidateData.indexOf(req.baseUrl)>-1)return next();
// if((jwt.verify(req.headers.authorization||'')||{}).success)return next();
// return res.json({success:false,code:500,msg:'token验证失败'});
// })//初始化路由
require('./routes/index')(router);app.use('/', router);
app.listen(port,()=>{console.log('http://localhost:'+port);
})
六、新建上传文件test.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>test upload</title>
</head>
<body><input type="file" onchange="goUpload(this.files)"><script type="text/javascript" src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script><script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.16.0/js/md5.js"></script><script>const reader = new FileReader();const instance = axios.create({baseURL: 'http://localhost:1990/', //后台接口url+porttimeout: 5000,headers: {'Content-Type': 'multipart/form-data',"authorization":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyTmFtZSI6Imxvbmdsb25nYWdvMSIsInBhc3N3b3JkIjoibG9uZzEyMzQ1NiIsImlhdCI6MTcwOTIwMjQ1NiwiZXhwIjoxNzA5MjA2MDU2fQ.XztBbjm2BbeQB7-OIXX040xuNxR5gnioCCgNV2c5NGI",},withCredentials: false, // defaultresponseType: 'json', // defaultmaxContentLength: 2000,});//上传文件计数对象let postFileObj = {};//上传切片计数对象let postChunkObj = {};//组件上传文件const goUpload = (files)=>{postFileObj = {};postChunkObj = {};upload(files[0])}//上传文件const upload = async (file)=>{//文件blobconst fileBlob = file.slice(0,file.size);//文件hash,用作后端新建临时文件夹名和上传完成的文件命名参数 并且,相同文件内容(文件名可不同)进行上传,生成的fileMD5是相同的,如有需要,可根据存储的文件名判断,不需要重新上传const fileMD5 = await blodToString(fileBlob);//切片数组const chunks = fileToChunks(file);//切片hash 用来命名生成的临时文件,并在所有切片上传完成,按顺序,读取切片,生成文件,如果不按照顺序,生成文件会因为写入顺序不对而乱码const chunkMD5Arr = [];//初始化当前文件上传计数if(!postFileObj[fileMD5])postFileObj[fileMD5] = {count:1}for(let i=0;i<chunks.length;i++){const chunkMD5 = await blodToString(chunks[i]);chunkMD5Arr.push(chunkMD5);//初始化key为fileMD5的切片组postChunkObj[fileMD5] = {};//初始化key为fileMD5的切片组中key为chunkMD5的切片计数对象postChunkObj[fileMD5][chunkMD5] = {success:false,count:1}await postChunk(chunks[i],fileMD5,chunkMD5);}let postAllChunk = true;//判断切片是否都已上传完成for(let key in postChunkObj[fileMD5]){if(!postChunkObj[fileMD5][key].success){postAllChunk = false;break}}// 所有切片都已上传成功if(postAllChunk){//调用merge方法const typeArr = file.name.split('.');mergeChunks(fileMD5,chunkMD5Arr,typeArr[typeArr.length-1]);}else{//有切片上传失败,重新执行上传postFileObj[fileMD5].count++;if(postFileObj[fileMD5].count>10) return console.log('文件上传失败');//重新上传await upload(file);}}//blob转换成stringconst blodToString = (blob)=>{return new Promise((resolve,reject)=>{reader.onloadend = (e)=>{resolve(md5(e.target.result));}reader.readAsText(blob);});}//file转换成chunksconst fileToChunks = (file)=>{//切片数组let chunks = [];//每个chunk大小const chunkSize = 1024*128;//转换成多少个chunkconst chunkCount = Math.ceil(file.size/chunkSize);for(let i=0;i<chunkCount;i++){if(i==chunkCount-1){chunks.push(file.slice(i*chunkSize,file.size));}else{chunks.push(file.slice(i*chunkSize,(i+1)*chunkSize));}}return chunks;}//上传切片const postChunk = (chunk,fileMD5,chunkMD5)=>{let fd = new FormData();//文件流的key与multer的single方法的参数param,必须保持一致,不一致接收不到文件流/*** fd.append('xxx',chunk);* multer().single('xxxx');* **/fd.append('chunk',chunk); fd.append('fileMD5',fileMD5);fd.append('chunkMD5',chunkMD5);let postCount = 0;return new Promise((resolve,reject)=>{instance.post('upload/upload',fd).then((res)=>{if(res.data&&res.data.success){postChunkObj[fileMD5][chunkMD5].success = true;resolve(console.log(`上传切片${chunkMD5}成功`));}else{//记录当前切片上传次数postChunkObj[fileMD5][chunkMD5].count++;//当前切片上传超过10次,终止上传if(postChunkObj[fileMD5][chunkMD5].count<11) { //上传失败,重新上传postChunk(chunk,fileMD5,chunkMD5);}}resolve(console.log(`上传切片${chunkMD5}失败`))})});}//合并多个chunkmergeChunks = (fileMD5,chunkMD5Arr,type)=>{// let fd = new FormData();// fd.append('chunk',Buffer.form('')); // fd.append('fileMD5',fileMD5);// fd.append('chunkMD5Arr',chunkMD5Arr);// fd.append('type',type);// instance.post('upload/merge',fd).then((res)=>{// });axios.post('http://localhost:1990/upload/merge',{fileMD5,chunkMD5Arr,type}).then((res)=>{});}</script></body>
</html>
七、开启jwt验证token,通过login接口生成token
添加用户
url:http://localhost:1990/user/login
name:/user/login
parans:{
“userName”: “longlongago1”,
“password”: “long123456”
}
总结
踩坑路漫漫长@~@
相关文章:

express+mysql+vue,从零搭建一个商城管理系统7--文件上传,大文件分片上传
提示:学习express,搭建管理系统 文章目录 前言一、安装multer,fs-extra二、新建config/upload.js三、新建routes/upload.js四、修改routes下的index.js五、修改index.js六、新建上传文件test.html七、开启jwt验证token,通过login接…...

markdown的使用(Typora)
文章目录 markdown的使用(Typora)一.标题二.段落格式2.1 换行2.2 分割线2.3 字体2.4 上下标2.5 脚注2.6 改变字体颜色 三.列表3.1 无序列表3.2 有序列表3.3 列表嵌套3.4 任务列表 四.区块五.代码显示5.1 行内代码5.2 代码块 六.链接七.图片八.表格九.表情符号大纲十、流程图10.…...

【python】json转成成yaml中文编码异常显示成:\u5317\u4EAC\u8DEF123\u53F7
姊妹篇:【python】json转成成yaml json数据 {"name": "张三","age": 30,"isMarried": false,"children": [{"name": "小王","age": 5},{"name": "小李",&qu…...

Python 实现Excel自动化办公(中)
在上一篇文章的基础上进行一些特殊的处理,这里的特殊处理主要是涉及到了日期格式数据的处理(上一篇文章大家估计也看到了日期数据的处理是不对的)以及常用的聚合数据统计处理,可以有效的实现你的常用统计要求。代码如下࿱…...

MCTS代码
这段代码的背景是玩一个游戏。游戏的参数有NUM_TURNS,在第i回合,你可以从一个整数[-2,2,3,-3]*(NUM_TURNS1-i)中进行选择。例如,在一个4回合的游戏中,在第1回合,你可以从[-8,8,12&am…...
Java 中notify 和 notifyAll 方法介绍
1. notify 方法 notify() 方法是 Java 中 Object 类的一个方法,它用来唤醒在该对象的监视器(monitor)上等待的单个线程。如果有多个线程都在该对象上等待,则会随机唤醒其中一个线程。被唤醒的线程将会尝试重新获取对象锁ÿ…...

Leetcode :杨辉三角
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中,每个数是它左上方和右上方的数的和。 思路:双循环,一个是层数,一个是当前数组的生成;两侧为1,需要边界判断条件…...

MWC 2024丨美格智能CEO杜国彬出席中国联通创新成果发布会并发表主题演讲
2月26日,中国联通在MWC2024 巴塞罗那期间举办了以“算网为基,智领未来”为主题的创新成果发布会,集中展示最新的创新成果与最佳实践。 中国通信标准化协会理事长闻库、GSMA首席财务官Louise Easterbrook、中国联通副总经理梁宝俊、华为ICT销…...

个人建站前端篇(七)vite + vue3企业级项目模板
一、vite命令行创建项目 npm create vitelatest根据提示选择模板,选择vite vue3 ts即可。 二、项目连接远程仓库 git init git remote add origin https://gitee.com/niech_project/vite-vue3-template.git git pull origin master git checkout -b dev三、项目…...

centos7 安装 docker-compose
1、直接参考官方: Install Compose standalone | Docker Docs 1、安装命令 curl -SL https://github.com/docker/compose/releases/download/v2.24.6/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose 2、修改 docker-compose 执行权限 不修改执行权…...

剑指offer面试题28:对称的二叉树
#试题28:对称的二叉树 题目: 请设计一个函数判断一棵二叉树是否 轴对称 。 示例 1: 输入:root [6,7,7,8,9,9,8] 输出:true 解释:从图中可看出树是轴对称的。示例 2: 输入:root …...

JS:原型与原型链(附带图解与代码)
一、原型 写在前面: 任何对象都有原型。 函数也是对象,所以函数也有原型。 1.什么是原型 在 JavaScript 中,对象有一个特殊的隐藏属性 [[Prototype]],它要么为 null,要么就是对另一个对象的引用,该对象…...

电子电器架构新趋势 —— 最佳着力点:域控制器
电子电器架构新趋势 —— 最佳着力点:域控制器 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师(Wechat:gongkenan2013)。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师…...

C++记录
常用快捷键: CTRL -向后定位 CTRL SHIFT -向前定位 1.注释:CTRLKC 2.取消注释:CTRLKU 11.调试(启动):F5 20.查找:CTRLF 21.替换:CTRLH 31.跳转到指定的某一行 1)方法1:组合键“CtrlG…...
ConcurrentModificationException并发修改异常
ConcurrentModificationException并发修改异常 原因分析 可以通过遍历索引也可以通过迭代器进行遍历。在我们使用迭代器进行遍历集合的时候,会获取到当前集合的迭代对象。在里面有封装了迭代器的remove方法与集合自带的remove方法,如果我们调用迭代器对…...

小程序事件处理
事件处理 一个应用仅仅只有界面展示是不够的,还需要和用户做交互,例如:响应用户的点击、获取用户输入的值等等,在小程序里边,我们就通过编写 JS 脚本文件来处理用户的操作 1. 事件绑定和事件对象 小程序中绑定事件与…...

蓝桥杯-单片机组基础6——定时计数器与外部中断混合使用(附小蜜蜂课程代码)
蓝桥杯单片机组备赛指南请查看这篇文章:戳此跳转蓝桥杯备赛指南文章 本文章针对蓝桥杯-单片机组比赛开发板所写,代码可直接在比赛开发板上使用。 型号:国信天长4T开发板(绿板),芯片:IAP15F2K6…...

交友社交软件开发-php交友聊天系统-
为了开发一个高效的交友系统,需要一个完善的信息管理和筛选机制。这个系统应该能够根据用户的个人信息、兴趣爱好、价值观等标准进行筛选,并向用户提供符合他们要求心仪的人的信息。为了实现这个目标,系统可以利用人工智能技术,分…...

vue2 开发记录
el-select 如何修改选择项的样式/el-select-dropdown__item 文字上下显示 测试代码 <div stylemargin-left: 100px><!-- 测试代码--><el-select filterablesizemini><div classxxx-el-select><el-optionv-foritem in [{key:1,des:2,…...

QML中表格中数据获取
1.在生成的动态表格中获取某格数据的内容 import QtQuick 2.15 import QtQuick.Window 2.15import QtQuick.Controls 2.0 import Qt.labs.qmlmodels 1.0 import QtQuick.Layouts 1.15Window {width: 640height: 480visible: truetitle: qsTr("Hello World")TableMod…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...
Python网页自动化Selenium中文文档
1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API,让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API,你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...