思路：使用nodejs搭建服务端，接收到客户端发送的问题后返回加载响应，然后调用ai接口，等待ai接口响应后将响应结果通过sse推送给客户端；使用react搭建页面，主要由问题输入框、发送按钮和问答信息展示模块步骤：创建node项目，安装相关依赖项npm init -y npm install express openai body-parser cors创建server.js文件，配置服务const express = require('express'); const bodyParser = require('body-parser'); const cors = require('cors'); const OpenAI = require("openai"); const app = express(); const port = 3001; app.use(cors()); app.use(bodyParser.json()); // deepseek 配置 // const openai = new OpenAI({ // baseURL: 'https://api.deepseek.com/v1', // apiKey: '你的key' // }); // 存储 SSE 客户端连接 const clients = new Map(); // SSE 端点 app.get('/stream', (req, res) => { const clientId = Date.now().toString(); res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); // 存储连接并发送客户端 ID clients.set(clientId, res); res.write(`event: clientId\ndata: ${clientId}\n\n`); // 心跳保持连接 const heartbeat = setInterval(() => res.write(': heartbeat\n\n'), 30000); req.on('close', () => { clearInterval(heartbeat); clients.delete(clientId); }); }); // 消息处理端点 app.post('/chat', async (req, res) => { const { clientId, message } = req.body; // 立即返回等待提示 res.status(200).send('AI 正在思考中...'); // 模拟 AI 接口调用 setTimeout(async () => { try { const aiResponse = `AI回复：${Math.random() * 100}`; // 替换为真实 AI 响应 const clientRes = clients.get(clientId); if (clientRes) { clientRes.write(`event: response\n`); clientRes.write(`data: ${JSON.stringify({ text: aiResponse })}\n\n`); } } catch (error) { const clientRes = clients.get(clientId); clientRes?.write(`event: error\ndata: ${error.message}\n\n`); } }, 2000); }); app.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); }); 搭建前端项目npx create-react-app ai-chat cd ai-chat npm install axios在src文件下，创建Chat.js文件，搭建简单聊天页面import React, { useState, useEffect } from 'react'; import axios from 'axios'; function Chat() { const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); const [clientId, setClientId] = useState(null); useEffect(() => { const es = new EventSource('http://localhost:3001/stream'); es.addEventListener('clientId', (e) => { setClientId(e.data); }); es.addEventListener('response', (e) => { const data = JSON.parse(e.data); setMessages(prev => [...prev, { text: data.text, type: 'Ai' }]); }); es.addEventListener('error', (e) => { console.error('SSE Error:', e.data); }); return () => { es.close(); }; }, []); const sendMessage = async () => { if (!input || !clientId) return; // 添加用户消息 setMessages(prev => [...prev, { text: input, type: 'user' }]); try { const response = await axios.post('http://localhost:3001/chat', { clientId: clientId, message: input }, { headers: { 'Content-Type': 'application/json', }, }); console.log('Server response:', response.data); // 假设服务器返回的消息格式为 setMessages(prev => [...prev, { text: response.data, type: 'Ai' }]); console.log('Messages:', messages); setInput(''); } catch (error) { console.error('Error while sending message:', error); } }; return ( setInput(e.target.value)} placeholder="输入你的消息..." /> 发送 {messages.map((message, index) => ( {message.type}: {message.text} ))} ); } export default Chat;修改App.js文件，引入搭建好的聊天页面import './App.css'; import Chat from './Chat'; function App() { return ( <Chat /> ); } export default App;在server.js同级目录下，启动服务node server.js在ai-chat文件夹下，启动前端项目npm start在浏览器中访问：http://localhost:3000 即可看到简单的聊天界面，发送消息后能收到服务端使用sse推送的消息，替换为真实的ai请求即可与AI聊天
写在前面：因为每隔一段时间需要查看历史数据，检查每天缺少数据几条数据和具体缺少数据的时间（每天24条数据，00-23），所以写一个工具减少工作时间（增加摸鱼时间），数据是从系统上导出的excel，如下图（数据为假数据）：实现思路：读取excel数据，获取时间，放入数组写一个方法，生成这个时间段的完整小时数据数组遍历完整的时间数组，查找出缺少的时间，统计缺少数量和具体时间段写入excel代码const XLSX = require('xlsx'); let fileDataList = [[ '离线时间', '离线时长', '离线时间段' ]] const fileName = 'gaoxinqu' // 将JSON对象写入Excel function writeExcel(arrayData, fileName) { console.log('开始写入Excel文件', fileName); // 创建一个工作簿 let WorkSheet = XLSX.utils.aoa_to_sheet(arrayData); // 创建一个工作表 let new_workbook = XLSX.utils.book_new(); // 将数组数据写入工作表 XLSX.utils.book_append_sheet(new_workbook, WorkSheet, '第一页'); // 将工作簿写入文件 XLSX.writeFile(new_workbook, fileName); console.log('写入Excel文件成功!'); } // 读取excel文件 const readExcel = () => { // 读取Excel文件 const workbook = XLSX.readFile(fileName + '.xlsx'); // 获取工作表的名字 const sheetNames = workbook.SheetNames; // 获取第一个工作表 const sheet = workbook.Sheets[sheetNames[0]]; // 将工作表转换为JSON对象 const data = XLSX.utils.sheet_to_json(sheet); // 读取到的数据 return data } // 读取excel文件 const excelData = readExcel(); // 转换excel的数据格式 const oldTimeList = excelData.map(item => { return item['采样时间'] }) // 生成的日期时间字符串数组 const generateYearlyHourlyDates = (startTime,endDate) => { // 创建一个空数组来存储日期时间字符串 let dateTimeArray = []; // 设定开始日期和结束日期 let startDate = new Date(startTime); let endDate = new Date(endDate); // 循环生成每小时的日期时间字符串 for (let currentDate = new Date(startDate); currentDate <= endDate; currentDate.setHours(currentDate.getHours() + 1)) { // 格式化日期时间字符串为"YYYY-MM-DD hh:00" let year = currentDate.getFullYear(); let month = String(currentDate.getMonth() + 1).padStart(2, '0'); let day = String(currentDate.getDate()).padStart(2, '0'); let hour = String(currentDate.getHours()).padStart(2, '0'); let formattedDateTime = `${year}-${month}-${day} ${hour}:00`; dateTimeArray.push(formattedDateTime); } // 生成的日期时间字符串数组 return dateTimeArray; } const yearlyHourlyDates = generateYearlyHourlyDates('2024-06-01T00:00:00','2024-06-30T23:00:00'); // 获取日期数组 // 找到每天缺少的小时数和具体的时间 const findMissingHours = () => { const missingHours = []; const tempMissingHours = {}; yearlyHourlyDates.forEach(date => { const [dateOnly, time] = date.split(' '); if (!oldTimeList.includes(date)) { if (!tempMissingHours[dateOnly]) { tempMissingHours[dateOnly] = [0, '']; } tempMissingHours[dateOnly][0]++; tempMissingHours[dateOnly][2] += (tempMissingHours[dateOnly][3] ? ',' : '') + time.split(':')[0]; } }); for (const [date, [count, hours]] of Object.entries(tempMissingHours)) { missingHours.push([date, count, hours]); } return missingHours; } fileDataList.push(...findMissingHours()); // 写入excel文件 writeExcel(fileDataList, fileName + '_result.xlsx');最终结果：
直播流播放实现技术栈：后端：node + ffmpeg + WebSocket前端：vue + fiv.js实现逻辑：后端启动WebSocket服务，根据ffmpeg工具实现实时转流，对实时流进行格式转换输出flv格式，前端使用flv.js插件播放flv格式的视频一、后端安装依赖包，fluent-ffmpeg是基于ffmpeg工具命令的依赖npm install fluent-ffmpeg ws websocket-stream --save安装ffmpeg二进制，不安装服务端会出现报错：An error occurred: Cannot find ffmpegnpm i @ffmpeg-installer/ffmpeg完整代码const WebSocket = require('ws') const webSocketStream = require('websocket-stream/stream') const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path const ffmpeg = require('fluent-ffmpeg') ffmpeg.setFfmpegPath(ffmpegPath) // 建立WebSocket服务 const wss = new WebSocket.Server({ port: 8881, perMessageDeflate: false }) // 监听连接 wss.on('connection', handleConnection) // 连接时触发事件 function handleConnection(ws, req) { // 获取前端请求的流地址（前端websocket连接时后面带上流地址） const url = req.url.slice(1) // 传入连接的ws客户端 实例化一个流 const stream = webSocketStream(ws, { binary: true }) // 通过ffmpeg命令 对实时流进行格式转换 输出flv格式 const ffmpegCommand = ffmpeg(url) .addInputOption('-analyzeduration', '100000', '-max_delay', '1000000') .on('start', function () { console.log('Stream started.') }) .on('codecData', function () { console.log('Stream codecData.') }) .on('error', function (err) { console.log('An error occured: ', err.message) stream.end() }) .on('end', function () { console.log('Stream end!') stream.end() }) .outputFormat('flv').videoCodec('copy') // .outputFormat('flv').videoCodec('copy').noAudio() // 取消音频输出 stream.on('close', function () { ffmpegCommand.kill('SIGKILL') }) try { // 执行命令 传输到实例流中返回给客户端 ffmpegCommand.pipe(stream) } catch (error) { console.log(error) } }二、前端安装依赖包npm install flv.js --save完整代码 播放 三、测试url公网 rtmp 测试地址：邓紫棋 多美丽mv (非常流畅秒开级别，强烈推荐) 地址：rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid (可用) 伊拉克 Al Sharqiya 电视台 (有卡顿延迟) 地址：rtmp://ns8.indexforce.com/home/mystream (可用) 韩国GOOD TV (不推荐太卡，很慢) 地址：rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp (可用)公网 m3u8 测试地址：http格式地址计时器 m3u8 (可用) http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8 计时器 m3u8 (可用) http://devimages.apple.com/iphone/samples/bipbop/gear3/prog_index.m3u8 计时器 m3u8 (可用) http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8 大海 m3u8 (可用) http://kbs-dokdo.gscdn.com/dokdo_300/definst/dokdo_300.stream/playlist.m3u8https格式地址钢铁之泪 m3u8 (12:14分钟) (可用) https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8 fMP4 m3u8 (10:00分钟) (可用) https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8 钢铁之泪 MP4 m3u8 (12:14分钟) (可用) https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.mp4/.m3u8 实时 Akamai m3u8 (10:00分钟) (可用) https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8 实时 Akamai m3u8 (2:00分钟) (可用) https://moctobpltc-i.akamaihd.net/hls/live/571329/eight/playlist.m3u8 大熊兔 m3u8 (10:34分钟) (可用) https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8公网 flv 测试地址：大熊兔flv (可用)https://sample-videos.com/video123/flv/720/big_buck_bunny_720p_1mb.flv https://sample-videos.com/video123/flv/720/big_buck_bunny_720p_2mb.flv https://sample-videos.com/video123/flv/720/big_buck_bunny_720p_5mb.flv https://sample-videos.com/video123/flv/720/big_buck_bunny_720p_10mb.flv https://sample-videos.com/video123/flv/720/big_buck_bunny_720p_20mb.flv公网 mkv 测试地址：已测试 (可用)https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_1mb.mkv https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_2mb.mkv https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_5mb.mkv https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_10mb.mkv公网 3gp 测试地址：已测试 (可用)https://sample-videos.com/video123/3gp/144/big_buck_bunny_144p_1mb.3gp https://sample-videos.com/video123/3gp/144/big_buck_bunny_144p_2mb.3gp https://sample-videos.com/video123/3gp/144/big_buck_bunny_144p_5mb.3gp https://sample-videos.com/video123/3gp/144/big_buck_bunny_144p_10mb.3gp公网 mp4 测试地址：已测试 (可用)http://www.w3school.com.cn/i/movie.mp4 http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4 http://vjs.zencdn.net/v/oceans.mp4 https://media.w3.org/2010/05/sintel/trailer.mp42023-11-24 更新(可用) 经测试上面mp4地址很卡，特此更新下面的地址https://v-cdn.zjol.com.cn/280443.mp4 https://v-cdn.zjol.com.cn/276982.mp4 https://v-cdn.zjol.com.cn/276984.mp4 https://v-cdn.zjol.com.cn/276985.mp4 https://media.w3.org/2010/05/sintel/trailer.mp4结尾：实现源码地址：https://gitee.com/jias0606/stream-to-video测试URL文章地址：https://blog.csdn.net/u014696856/article/details/134444974
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 }
Windows安装nvm：node版本控制工具1. nvm下载地址：https://github.com/coreybutler/nvm-windows/releases下载压缩包：nvm-setup.zip：安装版，推荐使用，下一步式傻瓜安装（注意：安装路径最好不要出现中文和空格，如果电脑上之前已经单独安装了node，先卸载）2. 安装nvm的路径自己可以更改安装路径，建议更改路径，不要选择Program Files或 Program Files (x86)，因为为它们命名有空格和()符号,这会导致node安装失败第一个选择的路径为nvm安装路径，第二个为nodejs安装路径最好将nvm安装在根目录下（D:\nvm D:\nvm\nodejs）安装完毕后输入 nvm -v 查看版本3. 使用，常用命令列出所有已经安装的Node版本nvm ls列出所有可以安装的Node版本号nvm list available安装指定版本号的Nodenvm install 14.20.0使用特定版本的Nodenvm use 14.20.0卸载版本号Nodenvm uninstall 14.20.04. 问题一. nvm切换node版本出现乱码用管理员运行控制台1不行的话直接卸载重新安装二. nvm命令正常，node -v 和npm -v 提示不是内部命令用管理员运行控制台正常的路径是 nvm下面有指定的node版本目录。node版本目录下会有npm。如果发现node包里没有下面的这些文件，就说明npm没有安装成功需要去官网查找对应版本，下载完整的包，把下载后的包，全部替换到D:\soft\nvm\v14.20.0 下面 即可node所有版本下载地址:https://nodejs.org/en/download/releases/三. 终端里运行nvm、node和npm命令正常，vscode报错重启vscode5. yarn切换node版本，执行安装命令即可nvm user 14.20.0 npm install -g yarn yarn -v
node.js文件操作node.js文件读取// 导入fs文件系统模块 const fs = require('fs'); // 调用 fs.readFile() 方法，读取文件内容 fs.readFile('./files/1.txt', 'utf8', function(err, dataStr) { // 判断文件是否读取成功 if (err) { return console.log('读取文件失败', err.message); } console.log('读取文件成功', dataStr); })node.js文件写入// 导入fs文件系统模块 const fs = require('fs'); // 调用 fs.writeFile() 方法，写入文件内容 /* 参数1：表示文件的存放路径 参数2：表示要写入的内容 参数3：读取文件的时候采用的编码格式一般默认为utf8 参数4：回调函数 */ fs.writeFile('./files/2.txt', 'Hello World Node.js', 'utf8', function(err) { // 如果文件写入成功，则err的值为null // 如果文件写入失败，则err的值是一个错误对象 if (err) { return console.log('文件写入失败', err.message); } console.log('文件写入成功'); })