当前位置: 首页 > news >正文

node mySql 实现数据的导入导出,以及导入批量插入的sql语句

node 实现导出, 在导出excel中包含图片(附件)

node 实现导出, 在导出excel中包含图片(附件)-CSDN博客icon-default.png?t=N7T8https://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中包含图片&#xff08;附件&#xff09; node 实现导出, 在导出excel中包含图片&#xff08;附件&#xff09;-CSDN博客https://blog.csdn.net/snows_l/article/details/139999392?spm1001.2014.3001.5502 一、效果 如图&#xff1a; 二、导入 …...

Webpack: 底层配置逻辑

概述 Webpack 5 提供了非常强大、灵活的模块打包功能&#xff0c;配合其成熟生态下数量庞大的插件、Loader 资源&#xff0c;已经能够满足大多数前端项目的工程化需求&#xff0c;但代价则是日益复杂、晦涩的使用方法&#xff0c;开发者通常需要根据项目环境、资源类型、编译目…...

数字图像处理期末复习题1

个人名片&#xff1a; &#x1f393;作者简介&#xff1a;嵌入式领域优质创作者&#x1f310;个人主页&#xff1a;妄北y &#x1f4de;个人QQ&#xff1a;2061314755 &#x1f48c;个人邮箱&#xff1a;[mailto:2061314755qq.com] &#x1f4f1;个人微信&#xff1a;Vir2025WB…...

poi-tl 生成 word 文件(插入文字、图片、表格、图表)

文章说明 本篇文章主要通过代码案例的方式&#xff0c;展示 poi-tl 生成 docx 文件的一些常用操作&#xff0c;主要涵盖以下内容 &#xff1a; 插入文本字符&#xff08;含样式、超链接&#xff09;插入图片插入表格引入标签&#xff08;通过可选文字的方式&#xff0c;这种方…...

centos上部署Ollama平台,实现语言大模型本地部署

网上有很多大模型&#xff0c;很多都是远程在线调用ChatGPT的api来实现的&#xff0c;自己本地是没有大模型的&#xff0c;这里和大家分享一个大模型平台&#xff0c;可以实现本地快速部署大模型。 Ollama是一个开源项目&#xff0c;它提供了一个平台和工具集&#xff0c;用于部…...

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

官方文档&#xff1a;Blank 目录标题...

如何使用Spring Boot进行单元测试

如何使用Spring Boot进行单元测试 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将探讨如何在Spring Boot项目中进行单元测试&#xff0c;确保代码质量…...

2024steam夏促商店打不开、steam活动加载不了解决方法一览

今年的夏促终于开始了!目前可以看到很多精品小游戏在促销列表内&#xff0c;活动正式开启后还不知道又会是怎样的一幅场景。因为每年夏促都会有不少刚高考完的新手加入&#xff0c;遇到常见的steam商店打不开、活动页面不加载等问题不知道怎么解决。所以这里给大家准备了几种常…...

IPC进程通信:QNX

引言 在现代操作系统中&#xff0c;进程间通信&#xff08;IPC&#xff09;机制是实现进程间数据交换和同步的关键技术。IPC允许多个进程共享信息和资源&#xff0c;从而协同工作完成复杂任务。在QNX Neutrino系统中&#xff0c;IPC尤为重要&#xff0c;因为QNX主要面向实时系…...

OpenCV学习之cv2.imshow()函数

OpenCV学习之cv2.imshow()函数 一、简介 cv2.imshow 是 OpenCV 库中用于显示图像的基本函数之一。在图像处理和计算机视觉的过程中&#xff0c;使用该函数可以快速预览处理后的图像&#xff0c;便于调试和结果展示。 二、基本语法 cv2.imshow(WindowName, Imgmat)三、参数说…...

Oracle、MySQL、PostGreSQL、SQL Server-空值

Oracle、MySQL、PostGreSQL、SQL Server-null value 最近几年数据库市场百花齐放&#xff0c;在做跨数据库迁移的数据库选型时&#xff0c;除了性能、稳定、安全、运维、功能、可扩展外&#xff0c;像开发中对于值的处理往往容易被人忽视&#xff0c; 之前写过一篇关于PG区别O…...

python pip详解1

一、简介 pip是python的一个软件包管理工具&#xff0c;同yum&#xff0c;apt作用一致&#xff0c;pip有两种使用方式&#xff1a;pip模块和pip命令&#xff0c;示例如下&#xff1a; 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&#xff1f; 传统开发方式&#xff0c;带来大量的低质量代码&#xff0c;而代码质量带来的问题&#xff1a; 1.在缺陷的泥潭中挣扎 开发长时间投入在缺陷的修复中&#xff0c;修复完依赖测试做长时间的回归测试 2.维护困难&#xff0c;开发缓慢 比如重复代码&am…...

huggingface镜像站

huggingface下载太慢&#xff0c;大模型文件太大。用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中如何实现数据库连接池优化&#xff1f; 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将深入探讨在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 什么是资源限制 并发编程的目的是为了让程序更快&#xff0c;大家都知道并不是开启的线程越多越快&#xff0c;因为开启的线程越多随即面临…...

Unity编辑器工具---版本控制与自动化打包工具

Unity - 特殊文件夹【作用与是否会被打包到build中】 Unity编辑器工具—版本控制与自动化打包工具&#xff1a; 面板显示&#xff1a;工具包含一个面板&#xff0c;用于展示软件的不同版本信息。版本信息&#xff1a;面板上显示主版本号、当前版本号和子版本号。版本控制功能…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...