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

使用Node.js的csvtojson转换CSV到JSON:去除表头并解决多文件读取异常

问题:csvtojson读取多个CSV文件时表头被当作数据解析

我使用Node.js的csvtojson库读取CSV文件并转换为JSON,项目文件夹中有两个数据内容相同的CSV文件,尝试读取它们并转换为JSON后作为响应返回。但遇到问题:读取完第一个文件后,第二个文件的首行(列表头)被当作数据解析,导致JSON中出现形如{"Col 1":"Col 1","Col 2":"Col 2"...}的错误条目。

错误JSON示例

[
    {
        "Col 1": "Test 1",
        "Col 2": "ABC 1",
        "Col 3": "DEF 1",
        "Col 4": "GHI 1"
    },
    {
        "Col 1": "Test 2",
        "Col 2": "ABC 2",
        "Col 3": "DEF 2",
        "Col 4": "GHI 2"
    }, 
    {
        "Col 1": "Test 3",
        "Col 2": "ABC 3",
        "Col 3": "DEF 3",
        "Col 4": "GHI 3"
    },
    {
        "Col 1": "Col 1", // **问题所在**
        "Col 2": "Col 2",
        "Col 3": "Col 3",
        "Col 4": "Col 4"
    },
    {
        "Col 1": "Test 1",
        "Col 2": "ABC 1",
        "Col 3": "DEF 1",
        "Col 4": "GHI 1"
    },
    {
        "Col 1": "Test 2",
        "Col 2": "ABC 2",
        "Col 3": "DEF 2",
        "Col 4": "GHI 2"
    }, 
    {
        "Col 1": "Test 3",
        "Col 2": "ABC 3",
        "Col 3": "DEF 3",
        "Col 4": "GHI 3"
    }
]

我的代码

import dotenv from 'dotenv';
import express from 'express';
import { Router } from 'express';
import fs from 'fs';
import { join } from "path";
import path from 'path';
import Converter from "csvtojson";

import fastcsv from 'fast-csv';

import Promise from 'bluebird';

Promise.promisifyAll(Converter.prototype);

var converter = new Converter();

const router = Router();
dotenv.config();

router.get("/readfile", (req, res, next) => {
    const currDir = path.join('./outputfolder/');
    console.log('currdir', currDir)

    function readFiles(dir, processFile) {
        // read directory
        fs.readdir(dir, (error, fileNames) => {
            if (error) throw error;

            fileNames.forEach(filename => {
                // get current file name
                const name = path.parse(filename).name;
                // get current file extension
                const ext = path.parse(filename).ext;
                // get current file path
                const filepath = path.resolve(dir, filename);

                // get information about the file
                fs.stat(filepath, function (error, stat) {
                    if (error) throw error;

                    // check if the current path is a file or a folder
                    const isFile = stat.isFile();

                    // exclude folders
                    if (isFile) {
                        // callback, do something with the file
                        processFile(filepath, name, ext, stat);
                    }
                });
            });
        });
    }

    readFiles(currDir, (filepath, name, ext, stat) => {
        // console.log('file path:', filepath);
        console.log('file name:', name);
        let csvData = [];
        converter
            .fromFile(filepath)
            .then((jsonObj) => {
                console.log(jsonObj);
                jsonObj.forEach(res => {
                    if (res.UID !== '') {
                        fs.writeFileSync('2pac.txt', JSON.stringify(jsonObj), (err) => {
                            // throws an error, you could also catch it here
                            if (err) throw err;
                        });
                    }
                })
                res.send(jsonObj)
            })

    });

})

export { router };

解决方案

核心问题

你复用了同一个Converter实例。csvtojson的Converter实例在处理第一个文件后会保留表头解析状态,处理第二个文件时会跳过表头检测,直接把首行当作数据条目解析,导致表头被混入结果。

修复后的代码

import dotenv from 'dotenv';
import express from 'express';
import { Router } from 'express';
import fs from 'fs';
import path from 'path';
import Converter from "csvtojson";
import Promise from 'bluebird';

Promise.promisifyAll(Converter.prototype);

const router = Router();
dotenv.config();

router.get("/readfile", async (req, res, next) => {
    const currDir = path.join('./outputfolder/');
    console.log('currdir', currDir);

    // 改用异步方式读取目录和文件,方便收集所有结果
    async function readAllFiles(dir) {
        const fileNames = await fs.promises.readdir(dir);
        const allResults = [];

        for (const filename of fileNames) {
            const filepath = path.resolve(dir, filename);
            const stat = await fs.promises.stat(filepath);
            
            if (stat.isFile()) {
                // 每个文件创建新的Converter实例
                const converter = new Converter();
                const jsonObj = await converter.fromFile(filepath);
                
                console.log(`处理完成:${filename}`);
                allResults.push(...jsonObj);

                // 写入文件:改为追加模式避免覆盖,也可统一写入一次
                fs.appendFileSync('2pac.txt', JSON.stringify(jsonObj) + '\n', (err) => {
                    if (err) console.error('写入文件失败:', err);
                });
            }
        }

        return allResults;
    }

    try {
        const finalJson = await readAllFiles(currDir);
        res.send(finalJson);
    } catch (err) {
        console.error('处理文件出错:', err);
        res.status(500).send('读取文件失败');
    }
})

export { router };

关键改动说明

  1. 每个文件新建Converter实例:彻底避免实例状态复用导致的表头解析错误
  2. 改用异步/await处理流程:简化异步逻辑,确保所有文件处理完成后再统一发送响应(原代码会在第一个文件处理完就调用res.send,导致后续响应失效)
  3. 修复变量名冲突:原代码中jsonObj.forEach(res => {})的res覆盖了路由响应对象,改为item避免冲突
  4. 优化文件写入逻辑:改用appendFileSync避免重复覆盖,也可等所有文件处理完成后一次性写入提升性能
  5. 增加全局错误捕获:统一处理文件读取和转换过程中的异常,返回合适的HTTP状态码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 08:21:02