You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建百万行CSV并通过React+Node展示?Node存库报错求助

问题解决与方案

1. 修复"Cannot access 'fs' before initialization"错误

错误根源是require(fs)里的模块名没加引号,必须用字符串'fs';另外你的自执行函数没加()调用,导致代码根本没执行。修正后的CSV生成代码:

const fs = require('fs'); 

(async () => {     
    const writeStream = fs.createWriteStream('out.csv'); 
    // 改成百万行(1e8是1亿,会生成几十GB文件,资源消耗极大)
    for(let i=0; i< 1e6; i++){         
        const over = writeStream.write(`${i},1\n`); 
        if(!over){              
            await new Promise((resolve) => writeStream.once('drain', resolve));
        } 
    }     
    writeStream.end(); 
})(); // 必须加()执行自执行函数

2. 修正CSV读取代码并实现数据库存储

你的读取代码存在多处问题:chunkString在data事件外部无法访问、自执行函数未调用、readStream在函数作用域外部无法获取。下面是修正后的代码,同时集成MySQL批量存储(需先安装mysql2:npm install mysql2):

完整读取+存储代码

const fs = require('fs');
const mysql = require('mysql2/promise');

// 创建数据库连接池
const pool = mysql.createPool({
  host: 'localhost',
  user: '你的数据库用户名',
  password: '你的数据库密码',
  database: '你的数据库名',
  connectionLimit: 10, // 控制并发连接数
  queueLimit: 0
});

// 初始化数据表(首次运行执行)
async function initTable() {
  await pool.execute(`
    CREATE TABLE IF NOT EXISTS csv_data (
      id INT PRIMARY KEY,
      cost FLOAT NOT NULL
    )
  `);
}

(async () => {     
    await initTable();
    const readStream = fs.createReadStream('out.csv', 'utf8'); 
    let unprocessed = ''; 
    let batch = [];
    const BATCH_SIZE = 1000; // 批量插入,减少数据库请求次数

    readStream.on('data', async (chunk) => {         
        let chunkString = unprocessed + chunk.toString();         
        unprocessed = ''; 
        let startIndex = 0; 
         
        for(let ch = startIndex; ch < chunkString.length; ch++){             
            if(chunkString[ch] === '\n'){ 
                const line = chunkString.slice(startIndex, ch);
                const idx = line.indexOf(',');
                if(idx === -1) {
                    startIndex = ch + 1;
                    continue;
                }
                const id = parseInt(line.slice(0, idx));
                const cost = parseFloat(line.slice(idx + 1)); 
                batch.push([id, cost]);

                // 达到批量阈值就插入数据库
                if(batch.length >= BATCH_SIZE) {
                    await pool.execute(
                        'INSERT INTO csv_data (id, cost) VALUES ?',
                        [batch]
                    );
                    batch = [];
                }
                startIndex = ch + 1; 
            } 
        } 

        // 处理chunk末尾未完成的行
        const remaining = chunkString.slice(startIndex);
        if(remaining) unprocessed = remaining;
    });     
    
    readStream.on('end', async () => {     
        // 插入剩余的最后一批数据
        if(batch.length > 0) {
            await pool.execute(
                'INSERT INTO csv_data (id, cost) VALUES ?',
                [batch]
            );
        }
        console.log('所有数据已存入数据库');
        await pool.end();
    });

    readStream.on('error', (err) => {
        console.error('读取文件出错:', err);
    });
})();

3. 百万行CSV文件的高效创建

保持流写入的方式即可,流会逐行写入磁盘,不会一次性占用大量内存。注意不要轻易尝试1亿行,这会生成几十GB的文件,对存储和CPU压力极大,百万行是更合理的测试量级。

4. React + Node.js 展示百万行数据

直接渲染百万行会导致React卡顿甚至崩溃,必须用虚拟列表+后端分页的方案:

Node.js 后端(分页接口)

const express = require('express');
const mysql = require('mysql2/promise');
const cors = require('cors'); // 解决跨域
const app = express();
const port = 3001;

app.use(cors());
const pool = mysql.createPool({
  host: 'localhost',
  user: '你的数据库用户名',
  password: '你的数据库密码',
  database: '你的数据库名',
});

// 分页查询接口
app.get('/api/csv-data', async (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const pageSize = parseInt(req.query.pageSize) || 100;
  const offset = (page - 1) * pageSize;

  try {
    const [rows] = await pool.execute(
      'SELECT id, cost FROM csv_data LIMIT ? OFFSET ?',
      [pageSize, offset]
    );
    const [countResult] = await pool.execute('SELECT COUNT(*) as total FROM csv_data');
    res.json({
      data: rows,
      total: countResult[0].total,
      page,
      pageSize
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.listen(port, () => {
  console.log(`后端服务运行在 http://localhost:${port}`);
});

React 前端(虚拟列表实现)

先安装依赖:npm install react-window axios

然后编写展示组件:

import { useState, useEffect } from 'react';
import { FixedSizeList as List } from 'react-window';
import axios from 'axios';

function CsvDataViewer() {
  const [data, setData] = useState([]);
  const [total, setTotal] = useState(0);
  const [page, setPage] = useState(1);
  const pageSize = 100;

  useEffect(() => {
    fetchData(page);
  }, [page]);

  const fetchData = async (currentPage) => {
    try {
      const response = await axios.get(`http://localhost:3001/api/csv-data`, {
        params: { page: currentPage, pageSize }
      });
      setData(response.data.data);
      setTotal(response.data.total);
    } catch (err) {
      console.error('获取数据失败:', err);
    }
  };

  const Row = ({ index, style }) => {
    const item = data[index];
    return (
      <div style={{...style, display: 'flex', justifyContent: 'space-between', padding: '0 10px', borderBottom: '1px solid #eee'}}>
        <span>ID: {item.id}</span>
        <span>成本: {item.cost}</span>
      </div>
    );
  };

  return (
    <div style={{maxWidth: '800px', margin: '0 auto'}}>
      <h1>CSV数据展示</h1>
      <div style={{ height: '600px', border: '1px solid #ccc', borderRadius: '4px' }}>
        <List
          height={600}
          itemCount={data.length}
          itemSize={36}
          width="100%"
        >
          {Row}
        </List>
      </div>
      <div style={{display: 'flex', justifyContent: 'center', gap: '16px', marginTop: '16px'}}>
        <button onClick={() => setPage(prev => Math.max(prev-1, 1))} disabled={page === 1}>上一页</button>
        <span>第 {page} 页 / 共 {Math.ceil(total/pageSize)} 页</span>
        <button onClick={() => setPage(prev => prev+1)} disabled={page >= Math.ceil(total/pageSize)}>下一页</button>
      </div>
    </div>
  );
}

export default CsvDataViewer;

内容的提问来源于stack exchange,提问作者CoderInUi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 17:17:51