123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- const express = require('express');
- const multer = require('multer');
- const XLSX = require('xlsx');
- const path = require('path');
- const fs = require('fs');
- const uploadRouter = require('./routes/upload');
- const dotenv = require('dotenv');
-
- // 确保在其他代码之前加载环境变量
- const result = dotenv.config({
- path: path.resolve(__dirname, '.env')
- });
-
- if (result.error) {
- console.error('Error loading .env file:', result.error);
- }
-
- const app = express();
- const upload = multer({ dest: 'uploads/' });
-
- // 用于存储原始工作簿格式
- let originalWorkbook = null;
-
- // 配置中间件
- app.use(express.json({limit: '50mb'}));
- app.use(express.urlencoded({ extended: true, limit: '50mb' }));
- app.use(express.static(path.join(__dirname, 'public')));
-
- // 注册图片上传路由
- app.use('/', uploadRouter);
-
- // Excel文件上传处理
- app.post('/upload', upload.single('excelFile'), (req, res) => {
- if (!req.file) {
- return res.status(400).send('No file uploaded.');
- }
-
- // 读取并保存原始工作簿
- originalWorkbook = XLSX.readFile(req.file.path, {
- cellStyles: true,
- cellNF: true,
- cellFormula: true
- });
-
- const firstSheet = originalWorkbook.Sheets[originalWorkbook.SheetNames[0]];
-
- // 读取标题 (A1 到 AR1)
- const headers = [];
- for (let i = 0; i < 44; i++) {
- const cellRef = XLSX.utils.encode_cell({c: i, r: 0});
- const cell = firstSheet[cellRef];
- headers.push(cell ? cell.v : '');
- }
-
- // 读取数据并保留单元格格式信息
- const range = XLSX.utils.decode_range(firstSheet['!ref']);
- const data = [];
-
- for(let R = 1; R <= range.e.r; ++R) {
- const row = {};
- for(let C = 0; C <= range.e.c; ++C) {
- const cellRef = XLSX.utils.encode_cell({c: C, r: R});
- const cell = firstSheet[cellRef];
- if(cell) {
- row[headers[C]] = cell.v;
- }
- }
- data.push(row);
- }
-
- // 保存原始文件路径
- originalWorkbook.filePath = req.file.path;
-
- res.json({headers, data});
- });
-
- // 保存编辑后的数据
- app.post('/save', (req, res) => {
- try {
- const { headers, data } = req.body;
-
- // 检查超长内容
- const exceedingCells = [];
- data.forEach((row, rowIndex) => {
- headers.forEach((header, colIndex) => {
- const content = row[header];
- if (content && content.length > 32767) {
- exceedingCells.push({
- row: rowIndex + 2,
- column: header,
- length: content.length
- });
- }
- });
- });
-
- if (exceedingCells.length > 0) {
- const errorMessage = exceedingCells.map(cell =>
- `第 ${cell.row} 行的 "${cell.column}" 列内容长度为 ${cell.length},超出了Excel单元格32767个字符的限制`
- ).join('\n');
-
- return res.status(400).json({
- error: '存在超出Excel字符限制的单元格',
- details: exceedingCells,
- message: errorMessage
- });
- }
-
- if (!originalWorkbook) {
- return res.status(400).json({ error: 'No original workbook found. Please upload file first.' });
- }
-
- const originalSheet = originalWorkbook.Sheets[originalWorkbook.SheetNames[0]];
-
- data.forEach((row, rowIndex) => {
- headers.forEach((header, colIndex) => {
- const cellRef = XLSX.utils.encode_cell({ r: rowIndex + 1, c: colIndex });
- const value = row[header];
-
- if (value === undefined || value === null) {
- // 如果单元格值为空,则删除该单元格
- delete originalSheet[cellRef];
- } else {
- // 创建或更新单元格
- originalSheet[cellRef] = {
- t: 's', // 设置类型为字符串
- v: value.toString(), // 值
- w: value.toString(), // 格式化显示值
- h: value.toString(), // HTML显示值
- };
- }
- });
- });
-
- // 创建downloads目录(如果不存在)
- const downloadsDir = path.join(__dirname, 'downloads');
- if (!fs.existsSync(downloadsDir)) {
- fs.mkdirSync(downloadsDir);
- }
-
- // 保存文件
- const fileName = `edited_${Date.now()}.xlsx`;
- const filePath = path.join(downloadsDir, fileName);
-
- XLSX.writeFile(originalWorkbook, filePath, {
- bookType: 'xlsx',
- bookSST: false,
- type: 'file',
- cellStyles: true,
- cellNF: true,
- cellFormula: true
- });
-
- res.json({ downloadUrl: `/download/${fileName}` });
- } catch (error) {
- console.error('Save error:', error);
- res.status(500).json({ error: error.message });
- }
- });
-
- // 下载文件
- app.get('/download/:filename', (req, res) => {
- const filePath = path.join(__dirname, 'downloads', req.params.filename);
- res.download(filePath, req.params.filename, (err) => {
- if (!err) {
- // 下载完成后删除文件
- fs.unlinkSync(filePath);
- }
- });
- });
-
- // 错误处理中间件
- app.use((err, req, res, next) => {
- console.error(err.stack);
- res.status(500).json({ error: err.message || '服务器内部错误' });
- });
-
- // 清理函数:当服务器关闭时删除临时文件
- function cleanup() {
- if (originalWorkbook && originalWorkbook.filePath) {
- try {
- fs.unlinkSync(originalWorkbook.filePath);
- } catch (err) {
- console.error('Error cleaning up:', err);
- }
- }
- }
-
- // 监听进程退出事件
- process.on('SIGINT', () => {
- cleanup();
- process.exit();
- });
-
- process.on('SIGTERM', () => {
- cleanup();
- process.exit();
- });
-
- const port = process.env.PORT || 3100;
- app.listen(port, () => {
- console.log(`Server is running on port ${port}`);
- });
|