fs.readFile在Docker容器内部表现不同

时间:2016-03-30 15:49:59

标签: node.js docker async-await fs

以下代码在OSX上的dev中运行时从未失败,但每次都在生活在Docker容器中的生产环境中失败:

DataSyncController.js

let jsonFile = await FileSystemService.ParseCsvToJson(fileName);
if (!jsonFile.success)
        return res.json({ success: false });

let parsedJson = await FileSystemService.ParseJsonFile({ file: jsonFile.fileName });
if (!parsedJson.success)
    return res.json({ success: false });

FileSystemService.js

static async ParseJsonFile(params)
{
    return new Promise((resolve, reject) =>
    {
        try
        {
            fs.readFile(jsonFilePath, 'utf-8', (err, data) =>
            {
                if (err)
                {
                    console.log('fs.readFile() error: ', err);
                    resolve({ success: false });
                }

                var file = [];

                try
                {
                    // This fails every time in the Docker container
                    // Error is [Unexpected end of input]
                    // At this point I've seend `data` evaluate to '',
                    // <buffer >, undefined and partial JSON data
                    file = JSON.parse(data);
                }
                catch(e)
                {
                    console.log('ERROR parsing JSON file: ', e);
                    return resolve({ success: false });
                }

                // Do Stuff
                return resolve({ success: true });
            });
        }
        catch(exception)
        {
            console.error(exception);
            resolve({ success: false });
        }
    });
}

似乎在Docker容器中,JSON文件在读取时没有写入(例如,如果我将整个JSON.parse()块放在fs.readFile()中,则timeout可以正常工作并阻止它运行5秒),但我不知道这是怎么可能的,也不是为什么在Docker容器中就是这种情况,而不是在我的本地机器上。任何想法一如既往地非常感激。

更新

根据要求,这是实际将JSON文件写入磁盘的ParseCsvToJson方法的实现。请注意,在Docker容器的开发和生产中,写入的JSON文件虽然很大(大约4,400条记录),但看起来还不错。

var Converter = require("csvtojson").Converter;
var filePath = `${config.root}/server/uploadDir`;

static async ParseCsvToJson(fileName)
{
    return new Promise((resolve, reject) =>
    {
        try
        {
            let fileNameWithoutExtension = fileName.replace('.csv', '');
            const jsonFilePath = `${filePath}/${fileNameWithoutExtension}.json`;
            const csvFilePath = `${filePath}/${fileName}`;

            // The parameter false will turn off final result construction.
            // It can avoid huge memory consumption while parsing.
            // The trade off is final result will not be populated to end_parsed event.
            var csvConverter = new Converter({ constructResult: false, toArrayString: true });
            var readStream = fs.createReadStream(csvFilePath);
            var writeStream = fs.createWriteStream(jsonFilePath);
            readStream.pipe(csvConverter).pipe(writeStream);

            resolve({ success: true, fileName: `${fileNameWithoutExtension}.json` });
        }
        catch(exception)
        {
            console.error(exception);
            resolve({ success: false });
        }
    });
}

1 个答案:

答案 0 :(得分:0)

这是由于缺乏对Linux如何处理流/管道以及对csvtojson模块的文档的误解的理解。关于转换器的实例化:

var csvConverter = new Converter({ constructResult: false, toArrayString: true });

文档说:

  

参数false将关闭最终结果构造。   解析时可以避免大量内存消耗。   权衡是最终结果不会填充到end_parsed事件。

由于我不太了解的原因,在OSX上,这已经足够了:

var readStream = fs.createReadStream('path/to/csvfile.csv', { encoding: 'utf-8' });
var writeStream = fs.createWriteStream('path/to/jsonfile.json', { encoding: 'utf-8' });
readStream.pipe(csvConverter).pipe(writeStream);
resolve({ success: true, fileName: '.json' });

然而,在Linux系统上,文件并不总是完全写入或写入。因此,还需要以下内容:

csvConverter.on('end_parsed', () =>
{
    // The docs are unclear whether there is a possible error object
    // passed along here in the case of failure, but clearly mention 
    // that the constructed result will be unavailable, so just resolve 
    // the Promise
    resolve({ success: true, fileName: 'jsonfile.json' });
});

这可能是一个边缘情况,但希望将来能帮助其他人。