node

node-api

# 1. 强制删除文件夹

import { rmSync } from 'node:fs'
rmSync('dist', { recursive: true, force: true })

# 2. 图片批量重命名

const http = require('http')
const fs = require('fs')

const httpServer = http.createServer((request, response) => {
  response.writeHead(404)
  response.end()
})

fs.readdir('./Tiktok/', 'utf8', (err, data) => {
  data.map(item => {
    let name = item.split('.')
    let fristName = parseInt(name[0])
    console.log('name', name)
    fs.rename(
      `./Tiktok/${fristName}.png`,
      `./test/${fristName + 108}.png`,
      err => {
        if (err) {
          console.error(err)
          res.status(500).send('file opration error').end()
        }
      }
    )
  })
})

httpServer.listen(8080, () => {
  console.log('[' + new Date() + '] Serveris listening on port 8080')
})

# 3. 图片上传到指定文件夹

const express = require('express')
const { formidable } = require('formidable')
const path = require('path')
const app = express()
const fs = require('fs')

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'index.html')) // 返回包含上传表单的 HTML 页面
})

// 处理文件上传的路由
app.post('/upload', async (req, res) => {
  let dest = 'uploads/'
  const form = formidable({
    uploadDir: dest,
    maxFileSize: 20 * 1024 * 1024,
    hashAlgorithm: 'md5'
  })
  let fields
  let files
  try {
    ;[fields, files] = await form.parse(req)

    files.file.forEach(f => {
      fs.renameSync(f.filepath, dest + f.originalFilename)
    })

    res.json({ ok: true, message: 'OK' })
  } catch (err) {
    console.error(err)
    res.json({ ok: false, error: err })
  }
})

app.listen(6000, () => {
  console.log('服务器已启动,访问 http://localhost:6000')
})

# 4. node-mailer 发送邮件

https://juejin.cn/post/7053779525737398285  邮箱授权
 const nodemailer = require('nodemailer')
    let transporter = nodemailer.createTransport({
      service: 'qq', //发送者邮箱厂家 163、qq等
      auth: {
        user: '1045032607@qq.com', //邮箱
        pass: 'fcrbeei***c', //授权码(设置-POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务-POP3/SMTP服务 (如何使用 Foxmail 等软件收发邮件?),发送短信获取授权码
      },
    })
    let mailOptions = {
      from: '1045032607@qq.com', //发送者
      to: '316675462@qq.com', //接受者
      subject: '测试标题', //主题名
      text: '222', //文本
      html: `<h2>nodemailer基本使用</h2>`, //内容,
      // 附件
      /* attachments: [
        {
          //当前目录下的文件
          filename: '',
          path: '',
        },
      ], */
    }
    transporter.sendMail(mailOptions, function (err, info) {
      if (err) {
        console.log(err)
        return
      }
      console.log('发送成功')
    })

# 5. socket.io 介绍使用

https://github.com/aermin/vue-chat/blob/master/server/index.js

  • 5.1 mqtt 测试地址
mqtt://test.mosquitto.org
  • 5.2 websocket 测试地址
ws://echo.websocket.org/

# 6. 6.node 反向代理

  • 6.1 方法 1
const Koa = require('koa')

const app = new Koa()

const proxy = require('koa-server-http-proxy')

app.use(async (ctx, next) => {
  ctx.set('Access-Control-Allow-Origin', '*')
  ctx.set(
    'Access-Control-Allow-Headers',
    'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild'
  )
  ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS')
  if (ctx.method == 'OPTIONS') {
    ctx.body = 200
  } else {
    await next()
  }
})

app.use(
  proxy('/yunpos', {
    target: 'https://manage-gray.yingqianpos.com',
    pathRewrite: { '^/yunpos': '' },
    changeOrigin: true
  })
)

app.listen(8081)
  • 6.2 方法 2
const express = require('express')
const { createProxyMiddleware } = require('http-proxy-middleware')

const app = express()

app.use(
  '/yunpos',
  createProxyMiddleware({
    target: 'https://manage-gray.yingqianpos.com',
    changeOrigin: true,
    pathRewrite: {
      '^/yunpos': '' // rewrite path
    }
  })
)
app.listen(8081)

# 7. 获取 Windows 操作系统为 32位还是 64位

能准确获取到 32 位软件安装包操作系统在 64 运行的硬件为 64 位;注意 os 是无法准确获取到的

const arch =
  process.arch === 'x64' || process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')
    ? 'x64'
    : 'ia32'

# 8. 跨平台获取机器唯一 id;在一些机房获取的设备 id 都是一样的,导致统计数据不准确,不建议使用

npm i node-machine-id
import { machineId, machineIdSync } from 'node-machine-id'
async function getMachineId() {
  let id = await machineId((original = false)) // original true 获取原始 id, 如:98912984-c4e9-5ceb-8000-03882a0485e4  ;非原始id,如:c24b0fe51856497eebb6a2bfcd120247aac0d6334d670bb92e09a00ce8169365
}

# 9. 查询端口是否被占用

  1. 端口范围:TCP 端口范围是 1-65535,其中 1-1024 是系统保留端口(需管理员 /root 权限才能使用);
  2. 权限问题:检测 1-1024 端口时,可能因权限不足抛出 EACCES 错误,需以管理员 /root 身份运行 Node.js 程序;
  3. 跨平台兼容:net 模块方案完全跨平台,系统命令方案需区分 Windows/macOS/Linux

# 9.1. 第三方模块,下载量非常高

portfinder

tcp-port-used

# 9.2. 查找可用端口方案

import { exec } from 'child_process'
import os from 'os'

/**
 * 查询端口占用的进程详情
 * @param {number} port - 目标端口
 * @returns {Promise<Array<{pid: string, name: string, command: string}>>} - 占用进程列表
 */
export async function getPortProcess(port) {
  return new Promise((resolve, reject) => {
    let command = ''
    const platform = os.platform()

    // 根据操作系统拼接命令
    if (platform === 'win32') {
      // Windows:netstat -ano 显示所有端口连接,findstr 过滤目标端口
      command = `netstat -ano | findstr :${port}`
    } else if (platform === 'darwin' || platform === 'linux') {
      // macOS/Linux:lsof -i :端口 显示占用进程(需安装 lsof,Linux 可通过 yum install lsof 安装)
      command = `lsof -i :${port} | grep LISTEN`
    } else {
      reject(new Error(`不支持的操作系统:${platform}`))
      return
    }

    // 执行系统命令
    exec(command, (err, stdout, stderr) => {
      if (err) {
        // 命令执行失败(如端口未被占用,stdout 为空),返回空数组
        resolve([])
        return
      }
      if (stderr) {
        reject(new Error(`命令执行错误:${stderr}`))
        return
      }

      // 解析输出结果,提取 PID 和进程名
      const processes: Array<{ pid: string, name: string, command: string }> =
        []
      const lines = stdout.trim().split('\n')

      if (platform === 'win32') {
        // Windows 输出格式示例:TCP    0.0.0.0:3000    0.0.0.0:0    LISTENING       1234
        lines.forEach(line => {
          const parts = line.trim().split(/\s+/)
          const pid = parts[parts.length - 1] // PID 在最后一列
          processes.push({
            pid,
            name: '未知进程(Windows 需通过 PID 手动查询)',
            command: `PID: ${pid}(可通过任务管理器 -> 详细信息 查找)`
          })
        })
      } else {
        // macOS/Linux 输出格式示例:node    1234  user  12u  IPv4  12345    0t0  TCP *:3000 (LISTEN)
        lines.forEach(line => {
          const parts = line.trim().split(/\s+/)
          const name = parts[0] // 进程名(如 node、chrome)
          const pid = parts[1] // PID
          const command = line
          processes.push({ pid, name, command })
        })
      }
      resolve(processes)
    })
  })
}

# 9.2.1. 使用

const processes = await getPortProcess(3000)
if (!Array.isArray(processes)) {
  console.log('获取进程信息时出错')
  return
}
if (processes.length === 0) {
  console.log('端口未被占用')
  return
}
console.log(`端口 3000 被以下进程占用:`)
processes.forEach((proc, index) => {
  console.log(
    `[${index + 1}] 进程名:${proc.name},PID:${proc.pid},详情:${
      proc.command
    }`
  )
})

# 10. 端口占用检测,在原有端口号上+1 启动服务

serverInstance.on('error', err => {
  switch (err.code) {
    case 'EADDRINUSE':
      console.error(`错误:端口 ${PORT} 已被占用,请释放端口后重试`)
      // 可选:自动查找可用端口(进阶功能)
      findAvailablePort().then(availablePort => {
        console.log(`尝试使用可用端口 ${availablePort} 启动`)
        serverInstance.listen(availablePort)
      })
      break
    case 'EACCES':
      console.error(`错误:无权限绑定端口 ${PORT}(需要管理员权限)`)
      break
    default:
      console.error('服务启动异常:', err)
  }
  process.exit(1)
})

// 辅助函数:查找可用端口
function findAvailablePort(startPort = 3000) {
  return new Promise(resolve => {
    const tester = require('net').createServer()
    tester.on('error', () => findAvailablePort(startPort + 1).then(resolve))
    tester.listen(startPort, () => {
      tester.close()
      resolve(startPort)
    })
  })
}
上次更新: