首页 > 解决方案 > 发出在循环中写入文件并从临时目录中读取的问题

问题描述

我正在尝试从 API 下载报告,但发送回数据的方式是使用一个压缩文件夹,其中包含另一个文件夹,并且该文件夹中有十几个压缩 JSON 文件。为了清楚起见,它看起来像这样:

report.zip/
├── reportID/ <- this is a regular folder
│   ├── reportID_123.json.gz
│   ├── reportID_456.json.gz
│   ├── reportID_789.json.gz
│   └── reportID_159.json.gz

我正在尝试解压缩第一个文件夹,然后解压缩每个单独的文件,最后循环并读取每个 JSON 文件的内容并将它们添加到单个对象中。但我有两个问题。

第一个是,虽然此代码的第一部分在解压缩第一个文件夹并提取每个 JSON 文件的名称时有效,而第二部分在解压缩它们时有效,但每个解压缩的文件都与之前的文件完全相同(这不是t 手动完成整个操作时的情况)。

var zip = new AdmZip(tempFilePath);
var zipEntries = zip.getEntries(); // an array of ZipEntry records
const allZips = [];
const tempOutputPath = path.join(os.tmpdir(), 'output'); 

zip.extractAllTo(/*target path*/tempOutputPath, /*overwrite*/true);

zipEntries.forEach(function(zipEntry) {
    allZips.push(zipEntry.entryName);
});

console.log (allZips);
const allData = [];

for (var i = 0; i <= allZips.length; i++) {
    const zippedFileName = path.join(tempOutputPath, allZips[i]); 
    const finalOutputName = path.join(tempOutputPath, allZips[i].replace('.gz', ''));
    console.log(zippedFileName);
    const inp = fs.createReadStream(zippedFileName);
    const out = fs.createWriteStream(finalOutputName);
    inp.pipe(unzip).pipe(out);
    console.log('File piped successfully');
    console.log(finalOutputName);
    let rawData = fs.readFileSync(finalOutputName);
    let data = rawData.toString();
    console.log(data);
    allData.push(data);
}

第二个问题是,即使在循环时,它也只能从某些文件中实际提取数据,这似乎是随机的,因为代码和文件除了名称之外都是相同的。这可能与循环结束后,我得到以下错误有关,尽管循环结束也是我的代码的结束:

(node:15772) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 end listeners added. Use emitter.setMaxListeners() to increase limit
(node:15772) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 unpipe listeners added. Use emitter.setMaxListeners() to increase limit
(node:15772) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 drain listeners added. Use emitter.setMaxListeners() to increase limit
(node:15772) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added. Use emitter.setMaxListeners() to increase limit
(node:15772) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 close listeners added. Use emitter.setMaxListeners() to increase limit
(node:15772) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 finish listeners added. Use emitter.setMaxListeners() to increase limit
(node:15772) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 data listeners added. Use emitter.setMaxListeners() to increase limit
(node:15772) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined

最后一件事,如果它是相关的,当硬编码文件名而不是使用临时文件路径时,这一切似乎工作得更好(尽管仍然不完美)。不幸的是,我必须使用临时文件路径,因为这是一个不允许常规路径的云函数。

标签: javascriptnode.jsapi

解决方案


.pipe()是异步的。所以,这行代码:

inp.pipe(unzip).pipe(out);

在未来某个未知的时间结束。因此,您正在尝试使用以下内容读取输出文件:

fs.readFileSync(finalOutputName);

在您知道输出已经完成之前。如果您要为此使用流,那么您需要观察closewritestream 上的事件,这样您就可以知道它.pipe()已完全完成。您还应该注意error流上的事件以实施正确的错误处理。

在为事件实现一个监听器close以读取输出之后,按照您的代码编写方式,您必须等待所有流都获得它们的close事件才能使用allData,因为只有这样它才会包含所有数据。

在试图了解您的代码流程以提出替代方案时,我看到了这行代码:

inp.pipe(unzip).pipe(out);

但是,没有变量的定义unzip

此外,除了创建所有临时文件之外,了解这段代码的最终目标是有帮助的,因此我们或许可以提出更好的方法。


作为一个小的简化,您可以替换它:

const allZips = [];
zipEntries.forEach(function(zipEntry) {
    allZips.push(zipEntry.entryName);
});

有了这个:

const allZips = zipEntries.map(zipeEntry => zipEntry.entryName);

当我试图更好地理解这段代码时,您似乎正在尝试对您的两个流进行处理,而您.pipe()是将提取的文件复制到新位置。fs.copyFile()使用or可以更简单地做到这一点fs.copyFileSync()


推荐阅读