簡體   English   中英

NodeJS CSVReader - 帶有 csv-parse 的 createReadStream 返回空數組

[英]NodeJS CSVReader - createReadStream with csv-parse returns empty array

我有 CSV 閱讀器 class,它采用兩個參數(文件路徑和模型)。 文件路徑為.csv文件的路徑,model為model,即.Z628CB5675FF5288B3AF775FF528F3AFE7 數據將存儲在 output 陣列中。 它有一個問題,因為當我返回數組時,它是空的。 但是當我控制台記錄數組時,它里面有數據。 有人能幫我嗎?

索引.js

const parse = require('csv-parse')
const fs = require('fs');
const output = [];

class CSVReader{
    static GetRecord(filePath, model){
        fs.createReadStream(filePath)
            .pipe(parse({columns: false, delimiter: ',', trim: true, skip_empty_lines: true}))
            .on('readable', function (){
                let record
                while (record = this.read()){
                    let city = model.create(record)
                    output.push(record)
                }
            })
            .on('end', function (){
                //console.log(output);
            })
        return output;
    }
}
module.exports = CSVReader;

我用 Jest 測試了文件,但它有一個問題,正如預期的那樣是 6,但我收到了 []

索引.test.js

const CSVReader = require('../src/Index');
const City = require('../src/Models/City')
test('Can Read CSV File', () => {
    let filePath  = 'data/worldcities.csv';
    let records = CSVReader.GetRecord(filePath, City);
    expect(records.length).toBe(6);
});

tl;博士:

這是一個異步調用,因此您不能只返回響應並期望它起作用。 您需要使用 Promise API 和異步 function。

是什么讓這個異步?

所有 Node.js fs API 都是異步的(不包括fs.*Sync功能)。

我如何使用 Promise?

您可以在Promise的頂部返回 Promise,然后傳遞回調:

return new Promise((resolve, reject) => { /* callback */ });

修復代碼

// all of this is fine
const parse = require('csv-parse')
const fs = require('fs');
// remove the const output = []; as this will cause problems

class CSVReader{
    // this needs to be async
    static async GetRecord(filePath, model){
        // return a promise
        return new Promise((resolve, reject) => {
            // assign output here (https://stackoverflow.com/a/66402114/14133230)
            const output = [];
            fs.createReadStream(filePath)
                .pipe(parse({columns: false, delimiter: ',', trim: true, skip_empty_lines: true}))
                .on('readable', function (){
                    let record
                    while (record = this.read()){
                        let city = model.create(record)
                        output.push(record)
                    }
                    // you may need to call WriteStream#close() here
                })
                .on('end', function (){
                    // output is available here
                    // resolve the promise
                    resolve(output);
                })
        });
    }
}
module.exports = CSVReader;

使用基於 Promise 的新 function

const CSVReader = require('../src/Index');
const City = require('../src/Models/City')
test('Can Read CSV File', () => {
    let filePath  = 'data/worldcities.csv';
    let records = CSVReader.GetRecord(filePath, City); // type Promise
    records.then((response) => { // when promise fills
        expect(response.length).toBe(6);
    });
});

第一個建議:刪除庫 class 中全局定義的“const output = []”。 這會給使用您的 static 方法的人造成很大的困惑。 您最終會在那里累積以前調用的結果。

我還建議:如果您不需要它來處理巨大的 csv 文件,那么您絕對可以將 go 用於同步版本,並擺脫異步加載的復雜性。

const parse = require('csv-parse/lib/sync');
const fs = require('fs');

class CSVReader{
    static GetRecord(filePath, model) {
        const csvContents = fs.readFileSync(filePath);
        const output = parse(csvContents, {columns: false, delimiter: ',', trim: true,     skip_empty_lines: true})
            .map(function (record) {
                return model.create(record);
            });
        return output;
    }
}
module.exports = CSVReader;
    

另一個可能有用的重組可能來自將工廠模式(您目前打算使用 CSVReader 實現)直接附加到模型的基本實現。 這將更清楚哪些模型可與 CSVReader 一起使用,以及 Model 的實現如何構造為可用於該模式。

基礎CSVModel提供通用listFromCSV功能和modelFromRecord實例化。

src/Models/CSVModel.js

const parse = require('csv-parse/lib/sync');
const fs = require('fs');

class CSVModel {
    static modelFromRecord(record) {
        var model = new this();
        model.record = record;
        return model;
    }
    static listFromCSV (filePath) {
        const csvContents = fs.readFileSync(filePath);
        return parse(csvContents, {columns: false, delimiter: ',', trim: true, skip_empty_lines: true})
            .map((record) => {
                return this.modelFromRecord(record);
            });
    }
}
module.exports = CSVModel;

City model 從 CSVModel 繼承listFromCSV工廠,並指定其與本地 model 屬性的數據關聯

src/Models/City.js

const CSVModel = require('./CSVModel');

class City extends CSVModel {
    static modelFromRecord(record) {
        var model = super.modelFromRecord(record);
        // assign properties of the CSV row to dedicated model properties
        [model.name, model.id, model.asd] = [record[0], record[1], record[2]];
        // or classical
        model.name = record[0]; 
        // ...
        return model;
    }
}
module.exports = City;

這允許使用具體的 Model Class 從 CSV 實例化列表。

test/csvmodel.test.js

const City = require('../src/Models/City');
test('Can Read City list', () => {
    let filePath  = 'data/worldcities.csv';
    let records = City.listFromCSV(filePath);
    expect(records.length).toBe(6);
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM