nodejs 實現Excel數據導入數據庫,以及數據庫數據導出excel接口(核心使用了multer和node-xlsx庫)

項目地址:https://gitee.com/LiangDouJun/nodejsExcel

一、實現效果

1、數據庫數據導出

2、excel導入

二、代碼實現

// 根據環境加載對應的配置文件
const env = process.env.NODE_ENV || 'development';
require('dotenv').config({ path: `.env.${env}` });const express = require('express');
let multer = require('multer');
let moment = require('moment');
const xlsx = require("node-xlsx");
const fs = require('fs').promises; // 使用異步版本
const path = require('path');
const router = express.Router()
const connection = require('../db/db')// 常量定義 - 從環境變量獲取,如果沒有則使用默認值
const ALLOWED_FILE_TYPES = ['.xlsx', '.xls'];
const MAX_FILE_SIZE = parseInt(process.env.MAX_FILE_SIZE) || 10 * 1024 * 1024; // 10MB
const MAX_FILES = parseInt(process.env.MAX_FILES) || 5;
const FILE_DELETE_DELAY = parseInt(process.env.FILE_DELETE_DELAY) || 60 * 1000; // 1分鐘
const BATCH_SIZE = 1000; // 批量插入大小// 文件類型驗證函數
const validateFileType = (file) => {const ext = path.extname(file.originalname).toLowerCase();return ALLOWED_FILE_TYPES.includes(ext);
};let Storage = multer.diskStorage({destination: (req, file, callback) => {// 指定當前這個文件存放的目錄callback(null, 'files'); },filename: (req, file, callback) => {console.log('fieldname', file.originalname);// 文件命名:當前時間戳 + "_" + 源文件名稱callback(null, new Date().getTime() + '_' + file.originalname); }
});// 文件上傳配置
let upload = multer({ storage: Storage,limits: {fileSize: MAX_FILE_SIZE,files: MAX_FILES},fileFilter: (req, file, cb) => {if (!validateFileType(file)) {return cb(new Error('不支持的文件類型,只支持 .xlsx 和 .xls 文件'), false);}cb(null, true);}
}).array('file', MAX_FILES);// 批量插入數據到數據庫
const batchInsertData = async (data) => {if (data.length === 0) return;const values = data.map(item => [item.id, item.userId, item.latitude, item.longitude, item.createTime]);const placeholders = values.map(() => '(?,?,?,?,?)').join(',');const sql = `INSERT INTO \`position\` (id, userId, latitude, longitude, createTime) VALUES ${placeholders}`;const flatValues = values.flat();try {const conn = await connection.promise();const [result] = await conn.execute(sql, flatValues);return result;} catch (error) {throw error;}
};// 解析Excel文件數據
const parseExcelData = (fileUrl) => {try {const sheets = xlsx.parse(fileUrl, { cellDates: true });const arr = [];sheets.forEach((sheet) => {for (let i = 1; i < sheet.data.length; i++) {const row = sheet.data[i];if (row && row.length >= 3) { // 確保至少有3列數據arr.push({id: Math.random().toString(36).substring(2, 15),userId: row[0] || '',latitude: row[1] || 0,longitude: row[2] || 0,createTime: moment().utc('+8:00').format('YYYY-MM-DD HH:mm:ss'),});}}});return arr;} catch (error) {throw new Error(`解析Excel文件失敗: ${error.message}`);}
};// 安全刪除文件
const safeDeleteFile = async (fileUrl) => {try {await fs.unlink(fileUrl);console.log(`文件已刪除: ${fileUrl}`);} catch (error) {console.error(`刪除文件失敗: ${fileUrl}`, error);}
};// 導入Excel
router.post('/loadExcel', function (req, res) {upload(req, res, async (err) => {if (err) {console.error('文件上傳錯誤:', err);return res.status(400).send({status: 1,message: err.message || '導入失敗',data: null,});}if (!req.files || req.files.length === 0) {return res.status(400).send({status: 1,message: '請選擇要上傳的文件',data: null,});}const fileUrl = req.files[0].path;try {// 解析Excel數據const arr = parseExcelData(fileUrl);if (arr.length === 0) {await safeDeleteFile(fileUrl);return res.send({ status: 0, message: '文件解析成功,但沒有有效數據', data: { list: [], total: 0 } });}// 批量插入數據const batches = [];for (let i = 0; i < arr.length; i += BATCH_SIZE) {batches.push(arr.slice(i, i + BATCH_SIZE));}for (const batch of batches) {await batchInsertData(batch);}// 延遲刪除文件setTimeout(() => {safeDeleteFile(fileUrl);}, FILE_DELETE_DELAY);res.send({ status: 0, message: '導入成功', data: { list: arr, total: arr.length } });} catch (error) {console.error('導入處理錯誤:', error);// 清理文件await safeDeleteFile(fileUrl);res.status(500).send({status: 1,message: `導入失敗: ${error.message}`,data: null,});}});
});// 導出Excel
router.get('/export', async function (req, res) {const sqlStr = 'SELECT userId, latitude, longitude FROM position ORDER BY createTime DESC';try {const conn = await connection.promise();const [data] = await conn.execute(sqlStr);const info = [["用戶", "經度", "緯度"],...data.map(({userId, latitude, longitude}) => [userId, latitude, longitude])];const sheetOptions = {'!cols': [{wch: 30}, {wch: 30}, {wch: 30}]};const buffer = xlsx.build([{ name: '位置數據', data: info }], { sheetOptions }); res.setHeader('Content-Type','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');// 使用英文文件名,確保兼容性const filename = `position_data_${moment().format('YYYY-MM-DD_HH-mm-ss')}.xlsx`;res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);res.end(buffer, 'binary');} catch (error) {console.error('導出錯誤:', error);res.status(500).send({status: 1,message: '導出失敗',data: null,});}
});module.exports = router;

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/93646.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/93646.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/93646.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

VUE2 學習筆記8 v-text/html/cloak/once/pre/自定義

除了之前已經介紹過的v-on v-bind v-for v-if v-show&#xff0c;vue還有很多其他的指令。v-textv-text是Vue內置指令。內置指令&#xff0c;是Vue內部定義好的&#xff0c;開發的時候直接拿來用就行了。v-text用于向其所在的標簽添加文本。<body><div id"root&q…

vue 使用postcss-pxtorem 實現適老化

1. 安裝依賴 npm install postcss-pxtorem -D2. 配置 Vite (vite.config.js) import { defineConfig } from vite import vue from vitejs/plugin-vue import postcsspxtorem from postcss-pxtoremexport default defineConfig({plugins: [vue()],css: {postcss: {plugins: [po…

Rust:高效錯誤處理工具 anyhow

Rust 的 anyhow 庫是一個專注于簡化錯誤處理的工具&#xff0c;特別適合應用程序開發場景。它通過統一的錯誤類型和便捷的 API&#xff0c;減少模板代碼&#xff0c;提升錯誤信息的可讀性。以下是其核心用法及示例&#xff1a;1. 安裝與基礎用法 在 Cargo.toml 中添加依賴&…

Solidity基礎(教程①-簡單數字存儲)

我們來嘗試一個超級簡單的智能合約&#xff0c;它只會做一件事情&#xff1a;存儲一個數字&#xff0c;并且讓我們能修改這個數字。最簡單的 Solidity 代碼// SPDX-License-Identifier: MIT pragma solidity ^0.8.0;// 定義一個智能合約&#xff0c;名字叫做 SimpleStorage (簡…

在 Web3 時代通過自我主權合規重塑 KYC/AML

1. 引言 前序博客有&#xff1a; Ligero 和 Ligetron 中的 MPC 和 ZKLigetron&#xff1a;Nim Network開發的針對AI的zkVMLigetron&#xff1a;基于MPC-In-The-Head范式的zkVM簡介谷歌采用 Ligero 構建其 ZK 技術棧 KYC&#xff08;了解你的客戶&#xff0c;Know Your Custo…

Linux kernel pinctrl子系統簡介

pinctrl(Pin Control)子系統是 Linux 內核中用于統一管理 SoC 引腳(Pin)功能配置的核心子系統,主要解決傳統引腳管理方式中存在的配置分散、驅動沖突、資源管理混亂等問題。尤其在嵌入式系統中,SoC 引腳通常支持多種復用功能(如 GPIO、UART、SPI、I2C、視頻接口等),pi…

web開發常見問題解決方案大全:502/503 Bad Gateway/Connection reset/504 timed out/400 Bad Request/401 Unauthorized

web開發常見問題解決方案大全&#xff1a;502/503 Bad Gateway&#xff0f;Connection reset&#xff0f;504 timed out&#xff0f;400 Bad Request&#xff0f;401 Unauthorized&#xff0f;403 Forbidden 在使用反向代理&#xff08;如 Nginx、HAProxy&#xff09;或正向代…

Vue 3 拖拽排序功能優化實現:從原理到實戰應用

一、引言&#xff1a;為什么需要拖拽排序&#xff1f;在現代Web應用中&#xff0c;交互體驗越來越受到重視。拖拽排序(Drag and Drop)作為一種直觀的用戶交互方式&#xff0c;被廣泛應用于&#xff1a;任務管理工具&#xff08;如Trello的任務卡片排序&#xff09;內容管理系統…

git 使用 rebase 刪除某次 提交

git刪除某次commit記錄 在Git中&#xff0c;要刪除某次commit記錄有幾種不同的實現方法&#xff1a; 方法一&#xff1a;使用git rebase命令和~標記 該方法適用于刪除最近的幾次commit記錄。 首先&#xff0c;使用以下命令查看你需要刪除的commit的記錄 git log找到你要刪除的c…

第2章 cmd命令基礎:常用基礎命令(2)

Hi~ 我是李小咖&#xff0c;主要從事網絡安全技術開發和研究。 本文取自《李小咖網安技術庫》&#xff0c;歡迎一起交流學習&#x1fae1;&#xff1a;https://imbyter.com 本節介紹的命令有時間與日期&#xff08;time/date&#xff09;、顯示目錄&#xff08;dir&#xff09;…

我從農村來到了大城市

從田埂到霓虹初到城市那天&#xff0c;行李箱的滾輪碾過柏油路的震動&#xff0c;和老家泥地上的拖沓感完全不同。站在天橋上往下看&#xff0c;車流像被打翻的調色盤&#xff0c;紅的黃的光在柏油畫布上流淌&#xff0c;我數了三遍才認清那是出租車和公交車的尾燈。第一個月總…

代碼隨想錄算法訓練營第三十六天

LeetCode.1049 最后一塊石頭的重量 II 題目鏈接 最后一塊石頭的重量II 題解 class Solution {public int lastStoneWeightII(int[] stones) {int len stones.length;int sum 0;for(int i 0;i<len;i) sum stones[i];int target sum / 2;int[] dp new int[target 1…

Apache Ignite 的監控與指標(Monitoring and Metrics)

這段文檔是關于 Apache Ignite 的監控與指標&#xff08;Monitoring and Metrics&#xff09; 的介紹&#xff0c;內容非常關鍵&#xff0c;尤其在生產環境中保障系統穩定性和性能時至關重要。 我們來一步步深入解析這段文字&#xff0c;幫助你徹底理解其含義和實際意義。&…

【ssh】ubuntu服務器+本地windows主機,使用密鑰對進行ssh鏈接

目錄1、服務器配置ssh2、本地主機秘鑰對3、上傳公鑰至服務器4、配置服務器的公鑰信息5、測試連接1、服務器配置ssh 使用的服務器系統為 ubuntu系統20.04 首先確認服務器是否已安裝SSH&#xff0c;已安裝的話會返回openssh 的相關信息&#xff0c;返回為空表示未安裝 dpkg -l …

Linux文件fd

文件理解 文件屬性內容 打開文件&#xff1a;本質是進程打開文件&#xff0c;文件沒被打開時候再磁盤上。 操作文件&#xff1a;本質是進程操作文件。 在操作系統內部&#xff0c;一定存在大量被打開的文件&#xff0c;會對其進行管理&#xff0c;每一個被打開的文件&#…

北京-4年功能測試2年空窗-報培訓班學測開-第六十四天-準備面試項目(焦慮)-同學開始面試

今日產出&#xff0c;整理自我介紹&#xff0c;繼續整理第一個項目&#xff0c;學習linux命令很焦慮啊很焦慮&#xff0c;很著急今天本打算結束第一個項目的&#xff0c;但是沒能夠&#xff0c;越說感覺越亂&#xff0c;讓同學聽我講&#xff0c;同學說&#xff0c;要聽睡著了于…

網絡是如何運轉的?——常見網絡協議與網絡分層模型

目錄 基本網絡協議 TCP&#xff08;傳輸控制協議&#xff09; 可靠傳輸&#xff1a;序列號確認應答重傳機制 序列號&#xff08;seq&#xff09; 確認應答&#xff08;ACK&#xff09; 超時重傳 三次握手與四次揮手 三次握手&#xff08;建立連接&#xff09; 四次揮手…

OpenAI放大招:ChatGPT學習模式上線,免費AI智能家教

目錄一、背景介紹二、學習模式是什么國內直接使用AI主流模型GPT-5也會第一時間同步更新。三、主要功能特點1、互動式提示2、分層次響應3、個性化支持4、知識檢查5、靈活切換四、學生如何使用學習模式1、訪問方式2、適用場景3、交互過程4、使用示例五、局限性1、依賴學生自覺性2…

設計模式:享元模式 Flyweight

目錄前言問題解決方案享元工廠結構代碼前言 享元是一種結構型設計模式&#xff0c;它摒棄了在每個對象中保存所有數據的方式&#xff0c;通過共享多個對象所共有的相同狀態&#xff0c;讓你能在有限的內存容量中載入更多對象。 問題 假如你希望在長時間工作后放松一下&#x…

Spring Boot容器化實戰:用官方OpenJDK鏡像極速啟動你的應用

前言 用 Docker 打包 Java 應用,尤其是 Spring Boot,簡直是開發者的超級利器。想象一下,你的程序就像勤快的外賣小哥,隨時待命,跑遍任何一臺機器,馬上為你服務。不論是開發環境還是生產環境,Docker 都能讓部署變得輕松又高效,徹底告別“環境不一致”的煩惱。 本篇文章…