node mySql 实现数据的导入导出,以及导入批量插入的sql语句
node 实现导出, 在导出excel中包含图片(附件)
node 实现导出, 在导出excel中包含图片(附件)-CSDN博客
https://blog.csdn.net/snows_l/article/details/139999392?spm=1001.2014.3001.5502
一、效果
如图:


二、导入
1、前端上传文件
// template
<el-form-item class="custom-item-skill-upoad" label="技能表文件" prop="file" style="width: 100%"><el-upload style="width: 100%" :limit="1" :auto-upload="false" :show-file-list="true" accept=".xlsx" :on-change="handleFileChange"><template #trigger><el-button size="small" type="primary">点击上传</el-button></template><template #tip><div class="el-upload__tip text-red">只能上传.xlsx文件</div></template></el-upload>
</el-form-item>
// api 导入技能
export function importSkill(file, append = 1, name = '') {let formData = new FormData();let suffix = file.name && file.name.split('.')[1];let defaultName = file.name && file.name.split('.')[0];let fileName = name ? name.replace(/[\u4e00-\u9fa5]/g, '') + '.' + suffix : defaultName.replace(/[\u4e00-\u9fa5]/g, '') + '.' + suffix;formData.append('file', file, fileName);return request({url: '/sys/skill/import/' + append,method: 'post',data: formData,headers: {'Content-Type': 'multipart/form-data'}});
}
/*** @script**/
const state = reactive({form:{file:null, // 用于存储上传的文件append:1 // 是否是追加导入 1:追加导入 2:覆盖导入}
})// 选择文件
const handleFileChange = file => {if (file) {state.form.file = file;formRef.value.validateField('file');}
};// 确认导入
const handleSubmitImport = () => {(formRef.value as any).validate((valid: boolean) => {if (valid) {importSkill(state.form.file.raw, state.form.append).then(res => {if (res.code === 200) {ElMessage.success('导入成功!');state.dialogVisible = false;getSkillListFn();} else {ElMessage.error('导入失败!' + res.message);}});}});
};
2、后端 node 需要使用到 multer 中间件 将文件上传到服务器,然后使用 exceljs 这个插件进行文件的解析文件,代码如下:
/*** @description: 上传技能* @param {Object} req 请求对象* @param {Object} res 响应对象* @param {Function} next*/
// 配置 multer 存储
const storage = multer.diskStorage({destination: function (req, file, cb) {cb(null, '../public/common');},filename: function (req, file, cb) {cb(null, 'skill_' + file.originalname);}
});
const upload = multer({ storage: storage });
// 导入技能
router.post('/skill/import/:append', upload.single('file'), (req, res) => {const workbook = new Excel.Workbook();const filePath = req.file.path;// 读取文件workbook.xlsx.readFile(filePath).then(async () => {const worksheet = workbook.getWorksheet(1);// data 为解析文件得到的数据const data = [];worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {if (rowNumber === 1) return; // 跳过表头const rowData = row.values.slice(1); // 去掉第一列的索引data.push(rowData);});...})});
3、最后拿到数据进行数据的批量插入
批量插入的sql语句如下
// 处理sql语句
let sql = 'INSERT INTO skill (name, level, description, type, effect, cost, duration, ranges, target) VALUES ?';
// data为解析到的数据
const params = data.map(item => [item[0], item[1], item[2], item[3], item[4], item[5], item[6], item[7], item[8]]);// 使用的时候
db.queryAsync(sql, [params]).then(result => {res.send({code: 200,data: data,msg: '导入成功'});}).catch(err => {console.log(err);res.send({code: 500,data: null,msg: '导入失败'});});
特别注意的是,插入一条的时候 sql 语句 values 后面紧跟的 () , 然后 () 中的参数个数以及参数要与前面的key一一对应,
当批量插入的时候: values 后面紧跟的是 [] , 然后数组 [] 中在是如同插入一条数据那样用 () , 一样 () 中的参数个数以及参数要与前面的key一一对应, 数组 [] 有多少个子项就插入多少条数据
1)、eg1:插入一条数据sql:
key与value一一对应 values 后紧跟()
let sql = 'INSERT INTO skill (name, level, description, type, effect, cost, duration, ranges, target) VALUES ('亚瑟王', '1', '老亚瑟', '1', '100', '200', '1s', '500', '周围敌人');';
2)、eg1:插入3条(批量)数据sql:
key与value一一对应 values 后紧跟[(), (), ()]
let sql = 'INSERT INTO skill (name, level, description, type, effect, cost, duration, ranges, target) VALUES [('亚瑟王1', '1', '老亚瑟', '1', '100', '200', '1s', '500', '周围敌人'), ('亚瑟王', '1', '老亚瑟2', '1', '100', '200', '1s', '500', '周围敌人'), ('亚瑟王3', '1', '老亚瑟', '1', '100', '200', '1s', '500', '周围敌人')];';
三、导出
导出就简单了 也是利用 exceljs 进行数据写入,直接上代码
router.post('/skill/export', (req, res) => {const { template } = req.body;const sql = 'SELECT * FROM skill';db.queryAsync(sql).then(async result => {const data = result.results;const workbook = new Excel.Workbook();const worksheet = workbook.addWorksheet('收入明细');// 设置表头// worksheet.addRow(['标题', '月份', '收入金额', '备注', '收入截图']);let baseTableTitle = [{ header: '技能名称', key: 'name', width: 12 },{ header: '技能等级', key: 'level', width: 10 },{ header: '技能描述', key: 'description', width: 20 },{ header: '技能类型', key: 'type', width: 12 },{ header: '技能效果', key: 'effect', width: 18 },{ header: '技能消耗', key: 'cost', width: 18 },{ header: '技能持续时间', key: 'duration', width: 20 },{ header: '技能范围', key: 'ranges', width: 20 },{ header: '技能目标', key: 'target', width: 20 }];worksheet.columns = baseTableTitle;// 如果不是模板,循环写入数据if (!template) {data.forEach(async (item, index) => {const rowData = worksheet.addRow([item.name, item.level, item.description, item.type, item.effect, item.cost, item.duration, item.ranges, item.target]);// 指定行高rowData.height = 50;});} else {// 如果下载模版 写入一条格式数据const rowData = worksheet.addRow(['大刀斩', '5', '技能描述', '大招', '亚瑟王那样的大招', '10000', '10', '500', '目标:亚瑟王']);// 指定行高rowData.height = 50;}const buffer = await workbook.xlsx.writeBuffer();// 处理中文文件名const realName = encodeURI('技能报表.xlsx', 'GBK').toString('iso8859-1');// 设置响应头res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');res.setHeader('Content-Disposition', 'attachment; filename=' + realName);// 发送Excel文件res.send(buffer);}).catch(err => {console.log(err);res.send({code: 500,msg: 'failed'});});
});
四、完整代码(整个文件)
/** @Description: ------------ fileDescription -----------* @Author: snows_l snows_l@163.com* @Date: 2024-06-26 10:20:25* @LastEditors: snows_l snows_l@163.com* @LastEditTime: 2024-06-26 18:06:52* @FilePath: /Website/Server/src/router/skill.js*/
const express = require('express');
const db = require('../../utils/connDB');
const Excel = require('exceljs');
const multer = require('multer');const router = express.Router();// 获取技能列表
router.get('/skill/list', (req, res) => {const { page = 1, size = 20, name, level } = req.query;let offset = (page - 1) * size;let sql = 'SELECT * FROM skill';let lenSql = `SELECT count('id') FROM skill`;if (name) {sql += ` WHERE name LIKE '%${name}%'`;lenSql += ` WHERE name LIKE '%${name}%'`;}if (level) {sql += ` ${name ? 'AND' : 'WHERE'} level = ${level}`;lenSql += ` ${name ? 'AND' : 'WHERE'} level = ${level}`;}sql += ` ORDER BY id ASC LIMIT ${size} OFFSET ${offset};`;db.queryAsync(lenSql).then(lenRes => {db.queryAsync(sql).then(result => {res.send({code: 200,data: result.results,total: lenRes.results[0]["count('id')"],msg: 'success'});}).catch(err => {console.log(err);res.send({code: 500,data: null,total: 0,msg: '系统异常, 请联系管理员'});});});
});// 导出技能
router.post('/skill/export', (req, res) => {const { template } = req.body;const sql = 'SELECT * FROM skill';db.queryAsync(sql).then(async result => {const data = result.results;const workbook = new Excel.Workbook();const worksheet = workbook.addWorksheet('收入明细');// 设置表头// worksheet.addRow(['标题', '月份', '收入金额', '备注', '收入截图']);let baseTableTitle = [{ header: '技能名称', key: 'name', width: 12 },{ header: '技能等级', key: 'level', width: 10 },{ header: '技能描述', key: 'description', width: 20 },{ header: '技能类型', key: 'type', width: 12 },{ header: '技能效果', key: 'effect', width: 18 },{ header: '技能消耗', key: 'cost', width: 18 },{ header: '技能持续时间', key: 'duration', width: 20 },{ header: '技能范围', key: 'ranges', width: 20 },{ header: '技能目标', key: 'target', width: 20 }];worksheet.columns = baseTableTitle;// 循环写入数据 如果不是模板,则默认写入数据if (!template) {data.forEach(async (item, index) => {const rowData = worksheet.addRow([item.name, item.level, item.description, item.type, item.effect, item.cost, item.duration, item.ranges, item.target]);// 指定行高rowData.height = 50;});} else {const rowData = worksheet.addRow(['大刀斩', '5', '技能描述', '大招', '亚瑟王那样的大招', '10000', '10', '500', '目标:亚瑟王']);// 指定行高rowData.height = 50;}const buffer = await workbook.xlsx.writeBuffer();// 处理中文文件名const realName = encodeURI('技能报表.xlsx', 'GBK').toString('iso8859-1');// 设置响应头res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');res.setHeader('Content-Disposition', 'attachment; filename=' + realName);// 发送Excel文件res.send(buffer);}).catch(err => {console.log(err);res.send({code: 500,msg: 'failed'});});
});/*** @description: 上传技能* @param {Object} req 请求对象* @param {Object} res 响应对象* @param {Function} next 中间件函数*/
// 配置 multer 存储
const storage = multer.diskStorage({destination: function (req, file, cb) {cb(null, '../public/common');},filename: function (req, file, cb) {cb(null, 'skill_' + file.originalname);}
});
const upload = multer({ storage: storage });
// 导入技能
router.post('/skill/import/:append', upload.single('file'), (req, res) => {const { append } = req.params;// 下一步function next(params) {const workbook = new Excel.Workbook();const filePath = req.file.path;// 读取文件workbook.xlsx.readFile(filePath).then(async () => {const worksheet = workbook.getWorksheet(1);const data = [];worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {if (rowNumber === 1) return; // 跳过表头const rowData = row.values.slice(1); // 去掉第一列的索引data.push(rowData);});// 处理sql语句let sql = 'INSERT INTO skill (name, level, description, type, effect, cost, duration, ranges, target) VALUES ?';const params = data.map(item => [item[0], item[1], item[2], item[3], item[4], item[5], item[6], item[7], item[8]]);db.queryAsync(sql, [params]).then(result => {res.send({code: 200,data: data,msg: '导入成功'});}).catch(err => {console.log(err);res.send({code: 500,data: null,msg: '导入失败'});});}).catch(err => {console.log(err);res.send({code: 500,data: null,msg: '导入失败'});});}// 如果是覆盖导入if (append == 2) {// 先清点所有数据let cleanSql = 'TRUNCATE TABLE skill;';db.queryAsync(cleanSql).then(() => {next();});} else {next();}
});module.exports = router;
相关文章:
node mySql 实现数据的导入导出,以及导入批量插入的sql语句
node 实现导出, 在导出excel中包含图片(附件) node 实现导出, 在导出excel中包含图片(附件)-CSDN博客https://blog.csdn.net/snows_l/article/details/139999392?spm1001.2014.3001.5502 一、效果 如图: 二、导入 …...
Webpack: 底层配置逻辑
概述 Webpack 5 提供了非常强大、灵活的模块打包功能,配合其成熟生态下数量庞大的插件、Loader 资源,已经能够满足大多数前端项目的工程化需求,但代价则是日益复杂、晦涩的使用方法,开发者通常需要根据项目环境、资源类型、编译目…...
数字图像处理期末复习题1
个人名片: 🎓作者简介:嵌入式领域优质创作者🌐个人主页:妄北y 📞个人QQ:2061314755 💌个人邮箱:[mailto:2061314755qq.com] 📱个人微信:Vir2025WB…...
poi-tl 生成 word 文件(插入文字、图片、表格、图表)
文章说明 本篇文章主要通过代码案例的方式,展示 poi-tl 生成 docx 文件的一些常用操作,主要涵盖以下内容 : 插入文本字符(含样式、超链接)插入图片插入表格引入标签(通过可选文字的方式,这种方…...
centos上部署Ollama平台,实现语言大模型本地部署
网上有很多大模型,很多都是远程在线调用ChatGPT的api来实现的,自己本地是没有大模型的,这里和大家分享一个大模型平台,可以实现本地快速部署大模型。 Ollama是一个开源项目,它提供了一个平台和工具集,用于部…...
Java学习 - Redis Redigo简单介绍
Redigo 驱动下载 go get github.com/garyburd/redigo/redis获取redis服务器连接 c, err : redis.Dial("tcp", "127.0.0.1:6379")if err ! nil {panic(err) }defer c.Close()命令使用 v, err : c.Do("SET","hello","world&quo…...
【鸿蒙学习笔记】ArkTS组件 Blank
官方文档:Blank 目录标题...
如何使用Spring Boot进行单元测试
如何使用Spring Boot进行单元测试 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将探讨如何在Spring Boot项目中进行单元测试,确保代码质量…...
2024steam夏促商店打不开、steam活动加载不了解决方法一览
今年的夏促终于开始了!目前可以看到很多精品小游戏在促销列表内,活动正式开启后还不知道又会是怎样的一幅场景。因为每年夏促都会有不少刚高考完的新手加入,遇到常见的steam商店打不开、活动页面不加载等问题不知道怎么解决。所以这里给大家准备了几种常…...
IPC进程通信:QNX
引言 在现代操作系统中,进程间通信(IPC)机制是实现进程间数据交换和同步的关键技术。IPC允许多个进程共享信息和资源,从而协同工作完成复杂任务。在QNX Neutrino系统中,IPC尤为重要,因为QNX主要面向实时系…...
OpenCV学习之cv2.imshow()函数
OpenCV学习之cv2.imshow()函数 一、简介 cv2.imshow 是 OpenCV 库中用于显示图像的基本函数之一。在图像处理和计算机视觉的过程中,使用该函数可以快速预览处理后的图像,便于调试和结果展示。 二、基本语法 cv2.imshow(WindowName, Imgmat)三、参数说…...
Oracle、MySQL、PostGreSQL、SQL Server-空值
Oracle、MySQL、PostGreSQL、SQL Server-null value 最近几年数据库市场百花齐放,在做跨数据库迁移的数据库选型时,除了性能、稳定、安全、运维、功能、可扩展外,像开发中对于值的处理往往容易被人忽视, 之前写过一篇关于PG区别O…...
python pip详解1
一、简介 pip是python的一个软件包管理工具,同yum,apt作用一致,pip有两种使用方式:pip模块和pip命令,示例如下: python -m pip install package pip install package二、命令行详解 python -m pip --hel…...
Linux常用命令大全(超详细!!!)
文章目录 1.Linux是什么1.1 关于Linux我们主要学习什么1.1 学习Linux常见命令的前置知识 2. Linux常见命令2.1 ls命令2.2 cd命令2.3 pwd命令2.4 touch命令2.5 cat命令2.6 echo命令2.7 vim命令2.8 mkdir 命令2.9 rm命令2.10 cp命令2.11 mv命令2.12 grep命令2.13 ps命令2.14 nets…...
TDD测试驱动开发
为什么需要TDD? 传统开发方式,带来大量的低质量代码,而代码质量带来的问题: 1.在缺陷的泥潭中挣扎 开发长时间投入在缺陷的修复中,修复完依赖测试做长时间的回归测试 2.维护困难,开发缓慢 比如重复代码&am…...
huggingface镜像站
huggingface下载太慢,大模型文件太大。用huggingface_hub镜像。 pip install -U huggingface_hub pip install huggingface-cli export HF_ENDPOINThttps://hf-mirror.com huggingface-cli download --resume-download shenzhi-wang/Llama3-8B-Chinese-Chat --loc…...
Java中如何实现数据库连接池优化?
Java中如何实现数据库连接池优化? 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨在Java应用程序中如何实现数据库连接池优化&am…...
002 SpringMVC入门项目搭建
文章目录 HelloController.javaspringmvc.xmlweb.xmlpom.xmlhello.jsp http://localhost:8080/showView http://localhost:8080/showData HelloController.java package com.springmvc.controller;import org.springframework.stereotype.Controller; import org.springframewo…...
为什么要使用多线程(并发编程)
目录 1.上下文的切换 1.1 什么是上下文切换 2. 并发编程的死锁问题 2.1 死锁产生的原因 2.2 避免死锁的方法 3.资源限制的挑战3.1 什么是资源限制 并发编程的目的是为了让程序更快,大家都知道并不是开启的线程越多越快,因为开启的线程越多随即面临…...
Unity编辑器工具---版本控制与自动化打包工具
Unity - 特殊文件夹【作用与是否会被打包到build中】 Unity编辑器工具—版本控制与自动化打包工具: 面板显示:工具包含一个面板,用于展示软件的不同版本信息。版本信息:面板上显示主版本号、当前版本号和子版本号。版本控制功能…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...
中国政务数据安全建设细化及市场需求分析
(基于新《政务数据共享条例》及相关法规) 一、引言 近年来,中国政府高度重视数字政府建设和数据要素市场化配置改革。《政务数据共享条例》(以下简称“《共享条例》”)的发布,与《中华人民共和国数据安全法》(以下简称“《数据安全法》”)、《中华人民共和国个人信息…...
NineData数据库DevOps功能全面支持百度智能云向量数据库 VectorDB,助力企业 AI 应用高效落地
NineData 的数据库 DevOps 解决方案已完成对百度智能云向量数据库 VectorDB 的全链路适配,成为国内首批提供 VectorDB 原生操作能力的服务商。此次合作聚焦 AI 开发核心场景,通过标准化 SQL 工作台与细粒度权限管控两大能力,助力企业安全高效…...
day51 python CBAM注意力
目录 一、CBAM 模块简介 二、CBAM 模块的实现 (一)通道注意力模块 (二)空间注意力模块 (三)CBAM 模块的组合 三、CBAM 模块的特性 四、CBAM 模块在 CNN 中的应用 一、CBAM 模块简介 在之前的探索中…...
在ubuntu等linux系统上申请https证书
使用 Certbot 自动申请 安装 Certbot Certbot 是 Let’s Encrypt 官方推荐的自动化工具,支持多种操作系统和服务器环境。 在 Ubuntu/Debian 上: sudo apt update sudo apt install certbot申请证书 纯手动方式(不自动配置)&…...
