1. 创建项目文件，然后执行初始化命令（会生成package.json项目描述文件，记录项目的信息，安装依赖包信息、相关命令等）node init -y2. 安装需要的依赖包/* * express: 快速搭建服务器 对于nodejs中http模块的进一步封装 * nodemon: 热重载 * cors: 处理跨域 * @hapi/joi: 请求数据进行验证 * @escook/express-joi: 表单数据进行验证 * express-jwt：解析 Token * mysql：数据库 * bcryptjs：非对称加密 * jsonwebtoken：生成 Token 字符串 */ npm add express nodemon cors @hapi/joi@17.1.0 @escook/express-joi express-jwt jsonwebtoken mysql3. 项目结构│ config.js │ index.js │ package-lock.json │ package.json │ ├─db │ index.js │ ├─routerHandle │ user.js │ userinfo.js │ ├─routers │ user.js │ userinfo.js │ └─schema user.js4. 执行文件 index.js// 导入模块 const express = require('express'); const cors = require('cors'); // 导入定义验证规则的包 const joi = require('joi'); const app = express(); app.use(cors()); app.use(express.json()) // 解析表单中json数据，不配置的情况下，req.body默认为undefined app.use(express.urlencoded({ extended: false })) // 解析表单中url-encoded数据，不配置的情况下，req.body默认为 {} // 失败响应方法中间件 app.use((req, res, next) => { res.cc = (status, err) => { res.send({ status, message: err instanceof Error ? err.message : err }) } next() }) // 路由之前声明中间件 // token 解析中间件 const expressJWT = require('express-jwt') const config = require('./config') app.use(expressJWT({ secret: config.jwtSecretKey }).unless({ path: [/^\/api/] })) // 导入用户登录注册路由模块 const userRouter = require('./routers/user') app.use('/api', userRouter) // 导入用户信息路由模块 const userInfoRouter = require('./routers/userinfo') app.use('/my', userInfoRouter) // 错误级别中间件，需放在所有路由最后 app.use((err, req, res, next) => { // 表单验证失败 if (err instanceof joi.ValidationError) return res.cc(400, err) // 身份认证失败 if (err.name === 'UnauthorizedError') return res.cc(401, '身份认证失败') res.cc(400, err) }) // 启动服务 app.listen(8081, () => { console.log('Server is running..., http://127.0.0.1'); })5. 数据库文件 db/index.jsconst mysql = require('mysql') const db = mysql.createPool({ host: '127.0.0.1', user: 'root', password: '123456', database: 'text' }) module.exports = db6. 登录注册路由模块 routers/user// 导入模块 const express = require('express'); const router = express.Router() // 导入用户路由处理函数模块 const userHandle = require('../routerHandle/user') // 导入验证表单中间件 const expressJoi = require('@escook/express-joi') // 导入需要验证的对象 const { registerValidate } = require('../schema/user') // 用户注册 router.post('/register', expressJoi(registerValidate), userHandle.register) // 用户登录 router.post('/login', expressJoi(registerValidate), userHandle.login) module.exports = router登录注册处理模块 routerHandle/user// 导入数据库操作模块 const db = require('../db') // 导入加密包 const bcrypt = require('bcryptjs') // 导入生成token的包 const jwt = require('jsonwebtoken') // 导入全局配置文件 const config = require('../config') // 用户注册处理模块 exports.register = (req, res) => { const userinfo = req.body if (!userinfo.username || !userinfo.password) { return res.cc(400, '请输入用户名和密码') } // 用户名查重 const DcSql = 'select * from node_users where username = ?' db.query(DcSql, userinfo.username, (err, results) => { if (err) { return res.cc(500, err) } if (results.length > 0) { return res.cc(400, '用户名已存在') } // 调用bcrypt.hashSync() 对密码进行加密 userinfo.password = bcrypt.hashSync(userinfo.password, 10) // 新增用户 const addSql = 'insert into node_users set ?' db.query(addSql, { username: userinfo.username, password: userinfo.password }, (err, results) => { if (err) { return res.cc(500, err) } if (results.affectedRows !== 1) { return res.cc(400, '注册失败') } // 注册成功 return res.cc(200, '注册成功') }) }) } // 用户登录处理模块 exports.login = (req, res) => { const usesrinfo = req.body; // 查询用户名是否存在 const querySql = 'select * from node_users where username = ?'; db.query(querySql, usesrinfo.username, (err, results) => { if (err) { return res.cc(500, err); } if (results.length !== 1) return res.cc(400, '用户名不存在') // 用户名存在的话校验密码是否正确 const comparPassword = bcrypt.compareSync(usesrinfo.password, results[0].password); if (!comparPassword) return res.cc(400, '密码错误') // 生成token字符串 const user = { ...results[0], password: '', user_pic: '' } const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: config.expiresin }) // 用户与密码正确则登陆成功 res.send({ status: 200, message: '登陆成功', token: 'Bearer ' + tokenStr }) }) }登录注册表单校验模块 schema/user// @ts-nocheck // 导入定义验证规则的包 const joi = require('joi'); // 定义登录注册验证规则 const username = joi.string().alphanum().min(3).max(8).required(); const password = joi.string().pattern(/^(?=.*\d)(?=.*[a-z])[a-zA-Z0-9]{6,12}$/).required(); exports.registerValidate = { body: { username, password } } // 定义用户信息修改验证规则 const id = joi.number().integer().min(1).required(); const nickname = joi.string(); const editUsername = joi.string().alphanum().min(3).max(8); const email = joi.string().email(); exports.updateUserInfoValidate = { body: { id, nickname, username: editUsername, email } } // 定义修改密码验证规则 exports.updatePasswordValidate = { body: { oldPwd: password, newPwd: joi.not(joi.ref('oldPwd')).concat(password), } } // 定义用户头像验证规则 const avatar = joi.string().dataUri().required(); exports.updateAvatarValidate = { body: { avatar } }7. 流程总结
实现思路：文件夹（datalog）下面有多个文件夹（20231123），下面是log.txt，循环遍历获取到log.txt，用split切割为数组，然后使用正则匹配代码对应的参数值，存放在数组中，最后写入excel│ TxtToExcel.js │ ├─dataLog │ │ HJ 212-2017 污染物在线监控（监测）系统数据传输标准.pdf │ │ │ ├─20231128 │ │ log.txt │ │ │ └─20231129 │ log.txt │代码：// 引入模块 const fs = require('fs'); const path = require('path'); const XLSX = require('xlsx') // 正则匹配 let typeHregex = /CN=(2061);/; // 小时数据 let type5Mregex = /CN=(2051);/; // 5分钟数据 let type1Mregex = /CN=(2011);/; // 1分钟数据 let Tregex = /DataTime=(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/; // 时间 let MN = /MN=(.*?);/; // 设备MN号 let PM10regex = /a34002-Avg=(.*?),/; let PM10regex1 = /a34002-Rtd=(.*?),/; let PM25regex = /a34004-Avg=(.*?),/; let PM25regex1 = /a34004-Rtd=(.*?),/; let Tspregex = /a34001-Avg=(.*?),/; // 悬浮颗粒物 let Wdregex = /a01001-Avg=(.*?),/; // 温度 let Sdregex = /a01002-Avg=(.*?),/; // 湿度 let Fsregex = /a01007-Avg=(.*?),/; // 风速 let Fxregex = /a01008-Avg=(.*?),/; // 风向 let Qyregex = /a01006-Avg=(.*?),/; // 气压 let SO2regex = /a21026-Avg=(.*?),/; // 二氧化硫 let SO2regex1 = /a21026-Rtd=(.*?),/; // 二氧化硫 let Coregex = /a21005-Avg=(.*?),/; // 一氧化碳 let Coregex1 = /a21005-Rtd=(.*?),/; // 一氧化碳 let NO2regex = /a21004-Avg=(.*?),/; // 二氧化氮 let NO2regex1 = /a21004-Rtd=(.*?),/; // 二氧化氮 let O3regex = /a05024-Avg=(.*?),/; // 臭氧 let O3regex1 = /o31104-Avg=(.*?),/; // 臭氧 let O3regex2 = /a05024-Rtd=(.*?),/; // 臭氧 let O3regex3 = /ac0001-Rtd=(.*?),/; // 臭氧 // 将JSON对象写入Excel function writeArrayToExcel(arrayData, fileName) { // 创建一个工作簿 let WorkSheet = XLSX.utils.aoa_to_sheet(arrayData); // 创建一个工作表 let new_workbook = XLSX.utils.book_new(); // 将数组数据写入工作表 XLSX.utils.book_append_sheet(new_workbook, WorkSheet, '第一页'); // 将工作簿写入文件 try { XLSX.writeFile(new_workbook, fileName) console.log('写入文件成功!') } catch (err) { console.log('写入文件失败!' + err) } } // 解析结果表头 let fileDataList = [[ '采样时间', '设备mn号', 'PM10', 'PM25', '二氧化硫SO2', '一氧化碳CO', '二氧化氮NO2', '臭氧O3', '悬浮颗粒物', '温度', '湿度', '风速', '风向', '气压', ]] // 正则解析 const jxFun = (item, typeRegex, mnValue) => { let result = Tregex.exec(item); if (!result[6]) return; if (!typeRegex.exec(item)) return; if (mnValue && /MN=(.*?);/.exec(item)[1] !== mnValue) return; let content = [ result ? `${result[1]}-${result[2]}-${result[3]} ${result[4]}:${result[5]}:${result[6]}` : '', MN.exec(item) ? MN.exec(item)[1] : '--', PM10regex.exec(item) ? PM10regex.exec(item)[1] : PM10regex1.exec(item) ? PM10regex1.exec(item)[1] : '--', PM25regex.exec(item) ? PM25regex.exec(item)[1] : PM25regex1.exec(item) ? PM25regex1.exec(item)[1] : '--', SO2regex.exec(item) ? SO2regex.exec(item)[1] : SO2regex1.exec(item) ? SO2regex1.exec(item)[1] : '--', Coregex.exec(item) ? Coregex.exec(item)[1] : Coregex1.exec(item) ? Coregex1.exec(item)[1] : '--', NO2regex.exec(item) ? NO2regex.exec(item)[1] : NO2regex1.exec(item) ? NO2regex1.exec(item)[1] : '--', O3regex.exec(item) ? O3regex.exec(item)[1] : O3regex1.exec(item) ? O3regex1.exec(item)[1] : O3regex2.exec(item) ? O3regex2.exec(item)[1] : O3regex3.exec(item) ? O3regex3.exec(item)[1] : '--', Tspregex.exec(item) ? Tspregex.exec(item)[1] : '--', Wdregex.exec(item) ? Wdregex.exec(item)[1] : '--', Sdregex.exec(item) ? Sdregex.exec(item)[1] : '--', Fsregex.exec(item) ? Fsregex.exec(item)[1] : '--', Fxregex.exec(item) ? Fxregex.exec(item)[1] : '--', Qyregex.exec(item) ? Qyregex.exec(item)[1] : '--', ] return content } function processing(data, searchType, mn) { let dataList = [] let arr = data.split('##').slice(1) if (searchType == 1 && mn) { console.log('小时 ' + mn + ' 数据'); arr.forEach(item => { if (jxFun(item, typeHregex, mn) !== undefined) { dataList.push(jxFun(item, typeHregex, mn)) } }) } else if (searchType == 2 && mn) { console.log('5分钟 ' + mn + ' 数据'); arr.forEach(item => { if (jxFun(item, type5Mregex, mn) !== undefined) { dataList.push(jxFun(item, type5Mregex, mn)) } }) } else if (searchType == 3 && mn) { console.log('1分钟 ' + mn + ' 数据'); arr.forEach(item => { if (jxFun(item, type1Mregex, mn) !== undefined) { dataList.push(jxFun(item, type1Mregex, mn)) } }) } else if (searchType == 1 && !mn) { console.log('小时数据'); arr.forEach(item => { jxFun(item, typeHregex, null) !== undefined if (jxFun(item, typeHregex, null) !== undefined) { dataList.push(jxFun(item, typeHregex, null)) } }) } else if (searchType == 2 && !mn) { console.log('5分钟数据'); arr.forEach(item => { if (jxFun(item, type5Mregex, null) !== undefined) { dataList.push(jxFun(item, type5Mregex, null)) } }) } else if (searchType == 3 && !mn) { console.log('1分钟数据'); arr.forEach(item => { if (jxFun(item, type1Mregex, null) !== undefined) { dataList.push(jxFun(item, type1Mregex, null)) } }) } return dataList } // 递归遍历获取文件 const getFilesRecursively = (directory) => { let filesAndDirectories = fs.readdirSync(directory); let fullPathFiles = []; filesAndDirectories.forEach((item) => { let fullPath = path.join(directory, item); let stats = fs.lstatSync(fullPath); if (stats.isDirectory()) { fullPathFiles = fullPathFiles.concat(getFilesRecursively(fullPath)); } else if (stats.isFile() && path.extname(fullPath) === '.txt') { fullPathFiles.push(fullPath); } }); return fullPathFiles; } // 同步读取文件内容 function readFileAsync(filePath) { return new Promise((resolve, reject) => { fs.readFile(filePath, 'utf8', (err, data) => { if (err) { reject(err); } else { resolve(data); } }); }); } // 文件夹路径 const folderPath = path.join(__dirname, './dataLog'); const filePathList = getFilesRecursively(folderPath); // 遍历读取并写入EXCEL async function txtToExcelAsync(filePathList) { for (const filePath of filePathList) { try { console.log('正在处理文件:', filePath); const data = await readFileAsync(filePath); fileDataList.push(...processing(data)); } catch (err) { console.log('读取文件失败!' + err); } } writeArrayToExcel(fileDataList, 'result.xlsx'); } txtToExcelAsync(filePathList) // 方法导出 module.exports = { txtToExcelAsync }