Skip to content

node读取大文件

js
import * as fs from 'fs';
import * as os from 'os';
import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';

// 配置常量
const FILE_DELIMITER = 'YOUR_FILE_DELIMITER';
const EOL = os.EOL;
const EOL_BUFFER = Buffer.from(EOL);
const DELIMITER_LINE = Buffer.from(FILE_DELIMITER + EOL);
const WORKER_COUNT = os.cpus().length;

// 主线程预处理函数
async function findDelimiterPositions(filePath: string) {
  const fileSize = (await fs.promises.stat(filePath)).size;
  const chunkSize = 1024 * 1024;
  const positions: number[] = [];
  let previousRemaining = Buffer.alloc(0);

  const fd = await fs.promises.open(filePath, 'r');
  try {
    for (let offset = 0; offset < fileSize;) {
      const buffer = Buffer.alloc(chunkSize + DELIMITER_LINE.length);
      const { bytesRead } = await fd.read(buffer, 0, chunkSize, offset);
      if (!bytesRead) break;

      const combined = Buffer.concat([previousRemaining, buffer.subarray(0, bytesRead)]);
      let pos = 0;

      while (pos < combined.length) {
        const idx = combined.indexOf(DELIMITER_LINE, pos);
        if (idx === -1) {
          previousRemaining = combined.subarray(pos);
          break;
        }
        positions.push(offset - previousRemaining.length + idx);
        pos = idx + DELIMITER_LINE.length;
      }

      offset += bytesRead;
    }
  } finally {
    await fd.close();
  }
  return positions;
}

// 工作线程处理逻辑
function workerProcess() {
  const { filePath, start, end, filePo } = workerData;
  const stream = fs.createReadStream(filePath, { start, end });
  const result: Buffer[] = [];
  let stationBuffer = Buffer.alloc(100);
  let tempBuffer = Buffer.alloc(5);
  let linePo = 0;

  // 字节处理状态机
  let state = 0; // 0: 正常行处理,1: 分隔符处理
  let buffer = Buffer.alloc(0);

  return new Promise<void>((resolve) => {
    stream.on('data', (chunk: Buffer) => {
      buffer = Buffer.concat([buffer, chunk]);

      while (true) {
       olIndex = buffer.indexOf(EOL_BUFFER);
        if (eolIndex === -1) break;

        const line = buffer.subarray(0, eolIndex);
        buffer = buffer.subarray(eolIndex + EOL_BUFFER.length);

        if (line.equals(DELIMITER_LINE.subarray(0, line.length))) {
          // 分隔符行直接跳过
          continue;
        }

        // 字节到字符串转换优化
        const transformed = processLine(line, linePo, filePo);
        result.push(Buffer.from(transformed + EOL));
        linePo++;
      }
    });

    stream.on('end', () => {
      parentPort.postMessage({
        filePo,
        data: Buffer.concat(result)
      });
      resolve();
    });
  });
}

// 优化的行处理函数
function processLine(lineBuffer: Buffer, linePo: number, filePo: number) {
  // 实现你的自定义转换逻辑
  return lineBuffer.toString() + `_processed_${filePo}_${linePo}`;
}

// 主线程逻辑
async function main() {
  const filePath = process.argv[2];
  const outputPath = process.argv[3];
  
  const delimiterPositions = await findDelimiterPositions(filePath);
  const fileSize = (await fs.promises.stat(filePath)).size;
  const parts = [];
  let prevPos = 0;

  // 生成处理区间
  for (const pos of delimiterPositions) {
    parts.push({ start: prevPos, end: pos, filePo: parts.length });
    prevPos = pos + DELIMITER_LINE.length;
  }
  parts.push({ start: prevPos, end: fileSize, filePo: parts.length });

  // 创建工作线程
  const workers = new Map();
  const results = new Map();
  
  parts.forEach((part, idx) => {
    const worker = new Worker(__filename, {
      workerData: { ...part, filePath }
    });
    
    worker.on('message', (msg) => {
      results.set(msg.filePo, msg.data);
      if (results.size === parts.length) {
        // 按顺序写入结果
        const sorted = Array.from(results.keys()).sort((a, b) => a - b);
        const ws = fs.createWriteStream(outputPath);
        sorted.forEach(po => ws.write(results.get(po)));
        ws.end();
      }
    });
    
    workers.set(idx, worker);
  });
}

// 启动入口
if (isMainThread) {
  main();
} else {
  workerProcess();
}

js判断当前环境是否是微信小程序环境

js
 window.navigator.userAgent.toLowerCase().includes('miniprogram/wx') || window.__wxjs_environment === 'miniprogram'

类中方法的this指向

js
class Person{
    constructor(name,age){
        this.name=name
        this.age=age
    }
    study(){
        console.log(this)
    }
}

const p = new Person('tom',18)
p.study()
const x = p.study
X() //属于函数的直接调用,输出this是 undefined

类中所定义的方法,局部都开启了严格模式,跟babel无关

fetch()添加超时功能

js
function createFetchWithTimeout(timeout = 1000){
    return (url,options)=>{
        return new Promise((resolve,reject) => {
            const signalController = new AbortController()//创建一个信号控制器 用来终端请求的
            fetch(url,{
                ...options,
                signal:signalController.signal
            }).then(resolve,reject)
            setTimeout(()=>{
                reject(new Error('fetch timeout'))
                //取消请求
                signalController.abort()
            },timeout)
        })
    }
}

网页文件下载(a标签)

html
<!-- 添加download属性 -->
<a :href="item.filePath" download>下载</a>

这种方式在ios会预览文件,而不是下载

js
   /**
     * @decription 下载兼容ios
     * @param {object} value
     * @param {object} value.filePath -文件地址
     * @param {object} value.fileName - 文件名
     */
   function download(value) {
      let link = document.createElement('a');
      link.href = value.filePath;
      link.download = value.fileName;
      // 在IOS上模拟点击链接
      if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
        var event = document.createEvent('MouseEvents');
        event.initMouseEvent(
          'click',
          true,
          true,
          window,
          1,
          0,
          0,
          0,
          0,
          false,
          false,
          false,
          false,
          0,
          null
        );
        link.dispatchEvent(event);
      } else {
        // 在其他设备上直接下载文件
        link.click();
      }
    }

静态资源统一导出

js
export { default as lishui } from './lishui.json';
export { default as lishuipng } from './lishui.png';

fetch获取json文件,拿到数据

js
let data = await fetch(lishui).then((response) => response.json());

去除对象结构中不想要的字段

js
// 原始对象 item
const item = {
    assets_id: 123,
    name: 'Example',
    description: 'This is an example description',
    price: 100
};

// 从 item 中去除 name 字段
const { name, ...newItem } = item;

console.log(newItem);

观察者IntersectionObserver

使用IntersectionObserver实现懒加载

js
//创建观察者
const ob = new IntersectionObserver(
  (entries) => {
    for (const entry of entries) {
      if (entry.isIntersecting) {
        const img = entry.target;
        img.src = img.dataset.src;
        ob.unobserve(img); //取消对这个img观察
      }
    }
  },
  {
    root: null, //观察元素跟谁的交叉,null表示视口 默认是null
    rootMargin: '0px', //对视口进行扩张大于0向外扩张,默认是0
    threshold: 0, //交叉多少才触发0~1
  }
);
const imges = document.querySelectorAll('img[data-src]')
imgs.forEach((img) => {
    ob.observe(img)
})

判断当前环境是否是微信小程序

js
 window.navigator.userAgent.toLowerCase().includes('miniprogram/wx');
 window.__wxjs_environment === 'miniprogram'

reflect的本质

惰性函数