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、前端上传文件

        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;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI商品图生成企业定制服务,广州这家公司走在了行业前头

🚀 最强AI绘图设计企业定制服务出炉 —— 触站A,智能艺术的革新者 🌟 🎨 触站AI,绘制未来的智能艺术 🎨在AI技术的浪潮中,触站AI以其前沿技术,为艺术与设计领域注入了新的活力。 &a…

【SkiaSharp绘图11】SKCanvas属性详解

文章目录 SKCanvas构造SKCanvas构造光栅 Surface构造GPU Surface构造PDF文档构造XPS文档构造SVG文档SKNoDrawCanvas 变换剪裁和状态构造函数相关属性DeviceClipBounds获取裁切边界(设备坐标系)ClipRect修改裁切区域IsClipEmpty当前裁切区域是否为空IsClipRect裁切区域是否为矩形…

C3P0数据库连接池

目录 一:连接池介绍 1.1连接池解决的问题 2.常用的数据库连接池 二:c3p0介绍 2.1C3P0介绍: 2.2C3P0快速入门 1.常用参数说明 2.API介绍 3.使用步骤 1.导入jar包c3p0-0.9.1.2.jar 2.编写c3p0-config.xml配置文件,配置对…

深入探索:大型语言模型消除幻觉的解决之道

随着人工智能技术的飞速发展,大型语言模型(LLMs)已经成为自然语言处理领域的明星。它们以其庞大的知识库和生成连贯、上下文相关文本的能力,极大地推动了研究、工业和社会的进步。然而,这些模型在生成文本时可能会产生…

27. 高级特性(下)

目录 一、为了类型安全和抽象而使用 newtype 模式二、使用类型别名创建类型同义词2.1 使用type关键赋予现有类型一个别名2.2 减少重复2.3 与Result<T, E>结合使用2.4 从不返回的 never type 三、高级函数和闭包3.1 函数指针3.2 返回闭包 四、宏4.1 宏和函数的区别4.2 mac…

2024 最新推广服务 API 推荐,助力业务腾飞

在数字化营销的浪潮中&#xff0c;API 服务正以其强大的功能和高效的特性&#xff0c;成为企业和开发者们实现精准推广、优化营销效果的得力助手。2024 年的今天&#xff0c;各种创新的 API 服务层出不穷&#xff0c;为广告投放、数据洞察等领域带来了前所未有的机遇。在接下来…

echarts隔行背景色

看了下使用说明&#xff0c;试了半天终于搞对了 参考文档&#xff1a;Documentation - Apache ECharts option {xAxis: {type: category,data: [Mon, Tue, Wed, Thu, Fri, Sat, Sun]},yAxis: {type: value},series: [{data: [120, 200, 150, 80, 70, 110, 130],type: bar,mar…

视频共享融合赋能平台LntonCVS视频监控业务平台建设安全煤矿矿井应用方案

随着我国经济的飞速增长&#xff0c;煤炭作为主要的能源之一&#xff0c;在我国的能源结构中扮演着至关重要的角色。然而&#xff0c;煤矿事故的频繁发生&#xff0c;不仅造成了巨大的人员伤亡和财产损失&#xff0c;也对社会产生了深远的负面影响。因此&#xff0c;实现煤矿的…

多家国产大模型提供OpenAI API服务替代方案,谷歌将推出明星网红AI聊天机器人

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 1、OpenAI终止对中国提供服务 6月25日凌晨&#xff0c;多个用户收到OpenAI的推送邮件&#xff0c;信中称&#xff0c;自今年7月9日起&#xff0c;将开始阻止来自非支持国家和地区的API&#xff08;应…

华为od-C卷200分题目3 - 两个字符串间的最短路径问题

华为od-C卷200分题目3 - 两个字符串间的最短路径问题 题目描述 给定两个字符串&#xff0c;分别为字符串A与字符串B。 例如A字符串为ABCABBA&#xff0c;B字符串为CBABAC可以得到下图m*n的二维数组&#xff0c;定义原点为(0, 0)&#xff0c;终点为(m, n)&#xff0c;水平与垂…

python自动化系列:自动将工作簿下的所有工作表合并到新工作表

作品介绍 作品名称&#xff1a;自动将工作簿下的所有工作表合并到新工作表 开发环境&#xff1a;PyCharm 2023.3.4 python3.7 用到的库&#xff1a;os、xlwings 作品简介&#xff1a;该实例使用xlwings库来操作Excel文件&#xff0c;其主要功能是将一个工作簿中所有工作表…

玩机进阶教程----MTK芯片使用Maui META修复基带 改写参数详细教程步骤解析

目前mtk芯片与高通芯片在主流机型 上使用比较普遍。但有时候版本更新或者误檫除分区等等原因会导致手机基带和串码丢失的故障。mtk芯片区别与高通。在早期mtk芯片中可以使用工具SN_Writer_Tool读写参数。但一些新版本机型兼容性不太好。今天使用另外一款工具来演示mtk芯片改写参…

打破数据分析壁垒:SPSS复习必备(十)

Means过程 统计学上的定义和计算公式 定义&#xff1a;Means过程是SPSS计算各种基本描述统计量的过程&#xff0c;其实就是按照用户指定条件&#xff0c;对样本进行分组计算均数和标准差&#xff0c;如按性别计算各组的均数和标准差。 用户可以指定一个或多个变量作为分组变…

上古世纪台服注册账号+下载客户端全方位图文教程

又一款新的MMRPG游戏即将上线啦&#xff0c;游戏名称叫做《上古世纪》游戏采用传统MMO类型游戏的玩法&#xff0c;但是开发商采用了先进的游戏引擎&#xff0c;让玩家们可以享受到极致的视觉体验。同时游戏的背景是建立在大陆分崩离析的基础上。各个部落因为领地的原因纷纷开战…

【大数据】—量化交易实战案例双均线策略(移动平均线)

声明&#xff1a;股市有风险&#xff0c;投资需谨慎&#xff01;本人没有系统学过金融知识&#xff0c;对股票有敬畏之心没有踏入其大门&#xff0c;今天用另外一种方法模拟炒股&#xff0c;后面的模拟的实战全部用同样的数据&#xff0c;最后比较哪种方法赚的钱多。 量化交易…

解决问题:浏览器中使用必应时提示“cn.bing.com将您的重定向的次数过多“

目录 一、问题分析二、关闭代理三、更新配置文件 一、问题分析 专业问题分析见其它博主的博文&#xff1a;重定向次数过多。 看了其它博文有一定启发&#xff0c;我自己尝试后发现两种解决办法。 二、关闭代理 我自己用的梯子是Clash&#xff0c;参考其他博主的分析&#x…

亚马逊运营专词(二)

1. A页面&#xff1a;亚马逊A页面即图文版商品详情页面&#xff0c;可以通过A页面使用不同的方式来描述商品特征&#xff0c;例如在页面中添加品牌故事、产品图片、产品文字介绍等&#xff0c;进一步完善页面。但目前A页面只对在亚马逊上注册了品牌的商家开放。 2. 跟卖&#x…

【PWN · TcachebinAttack | UAF】[2024CISCN · 华中赛区] note

一道简单的tcache劫持 一、题目 二、思路 存在UAF&#xff0c;libc版本2.31&#xff0c;经典菜单题 1.通过unsorted-bin-attack来leak-libc 2.通过uaf打tcache-bin-attack劫持__free_hook实现getshell 三、EXP from pwn import * context(archamd64,log_leveldebug)ioproce…

阿里提出MS-Diffusion:一键合成你喜爱的所有图像元素,个性化生成新思路!

文本到图像生成模型的最新进展极大地增强了从文本提示生成照片级逼真图像的能力&#xff0c;从而增加了人们对个性化文本到图像应用的兴趣&#xff0c;尤其是在多主题场景中。然而&#xff0c;这些进步受到两个主要挑战的阻碍&#xff1a; 需要根据文本描述准确维护每个参考主题…

ElasticSearch8.X查询DSL语法案例进阶实战

什么是Query DSL Query DSL主要由两部分组成&#xff1a;查询和过滤。 查询部分&#xff1a;用于指定搜索条件和匹配规则。例如&#xff0c;可以使用match查询进行全文检索&#xff0c;term查询进行精确匹配&#xff0c;range查询进行范围匹配等。过滤部分&#xff1a;用于对查…