如何使用Promises在fs.createReadStream/csv-parser中使用wait/async



我试图从Human Who Codes中实现这段代码,以及从这个答案中读取一个包含创意URL的CSV的启动方法,从媒体服务器下载创意,然后上传创意Facebook的Node.js SDK。然而,我在让Promise链在Node.js文件流中工作时遇到了问题。

以下是我的命令行脚本的参数:

Usage: creative-upload.js --inputFile --outputFile --adAccountId --uploadType --accessToken --creativeColumn --creativeIdColumn --creativeStatusColumn --maxRetries

这是我的脚本:

const csv = require('csv-parser');
const fs = require('fs');
const path = require('path');
const http = require('http');
const options = []; // removed yargs code, not important
// https://stackoverflow.com/a/49432604/904344
async function readStream(stream, encoding = "utf8") {
stream.setEncoding(encoding);
return new Promise((resolve, reject) => {
let data = [];

stream.on("data", chunk => data.push(chunk));
stream.on("end", () => resolve(data));
stream.on("error", error => reject(error));
});
}
// Here we wait for the myfunction to finish
// and then returns a promise that'll be waited for aswell
// It's useless to wait the myfunction to finish before to return
// we can simply returns a promise that will be resolved later
// Also point that we don't use async keyword on the function because
// we can simply returns the promise returned by myfunction
async function start() {
return await readStream(fs.createReadStream("test.csv").pipe(csv()));
}
// Call start
(async() => {
console.log('before start');
startPromise = start()
.then(data => {
for (var row of data) {
console.log(row);

const creative_url = row.creative_url;
const fileBasename = path.basename(creative_url);
const file = fs.createWriteStream("/tmp/" + fileBasename);
const request = http.get(creative_url, function(response) {
response.pipe(file);
});
// https://developers.facebook.com/docs/marketing-api/reference/ad-account/adimages/
if (uploadType == "image") {
let content = fs.readFileSync("/tmp/" + fileBasename).toString('base64');
const adimage = await account.createAdImage([], {
bytes: content
})
.then(() => {
console.log('Uploaded ' + fileBasename + " successfully.");
})
.catch((e) => {
throw e;
})
}
}
})
.catch(e => {
console.log(e);
});
console.log('after start ' + startPromise);
})();
process.exit(1);

这就是我得到的错误:

creative-upload.js:109
const adimage = await account.createAdImage([], {
^^^^^
SyntaxError: await is only valid in async function
at new Script (vm.js:80:7)
at createScript (vm.js:274:10)
at Object.runInThisContext (vm.js:326:10)
at Module._compile (internal/modules/cjs/loader.js:664:28)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
at Module.load (internal/modules/cjs/loader.js:600:32)
at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
at Function.Module._load (internal/modules/cjs/loader.js:531:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:754:12)
at startup (internal/bootstrap/node.js:283:19)

我去掉了async/await,这是我的新代码:

var promises=[];

fs.createReadStream(inputFile)
.pipe(csv())
.on('data', (row) => {
console.log(row);
const creative_url = row['Banner URL'];
console.log('Creative URL: ' + creative_url);

const fileBasename = path.basename(creative_url);
console.log('File Basename: ' + fileBasename);
const file = fs.createWriteStream("/tmp/" + fileBasename);
const request = http.get(creative_url, function(response) {
response.pipe(file);
});
console.log('Request: ' + request);

// https://developers.facebook.com/docs/marketing-api/reference/ad-account/adimages/
if (uploadType == "image") {
let content = fs.readFileSync("/tmp/" + fileBasename);
if (content == null || content.toString('base64') == '') {
console.log('ERROR! Could not get base64 content of tmp file.');
return;
}

/*{encoding: 'utf8'}, function(err, data) {
if (err) {
console.log('Error: ' + error);
return null;
} else {
return data.toString('base64');
}
});*/

content = content.toString('base64');
promises.push(account.createAdImage([], {
bytes: content
})
.then(() => {
console.log('Uploaded ' + fileBasename + " successfully.");
})
.catch((e) => {
console.log(e);
}));
}
// https://developers.facebook.com/docs/marketing-api/reference/ad-account/advideos/
else if (uploadType == "video") {
let content = fs.readFileSync("/tmp/" + fileBasename).toString('base64');
if (content == null || content.toString('base64') == '') {
console.log('ERROR! Could not get base64 content of tmp file.');
return;
}

content = content.toString('base64');
promises.push(account.createAdVideo([], {
bytes: content
})
.then(() => {
console.log('Uploaded ' + fileBasename + " successfully.");
})
.catch((e) => {
console.log(e);
}));
}
})
.on('end', () => {
Promise.all(promises);

console.log('CSV file successfully processed');
});

此代码成功上传了我CSV中的最后两个创意但是,所有其他行都无法维护内容变量,该变量在到达Facebook代码时为空。这是我的输出:

{ 'Banner Name': '...',
'Banner Size': '728x90',
'Banner URL':
'http://s3.amazonaws.com/beta-adv-cdn/jpeg_ads/c62cf4b9-4d86-4613-be15-b6c3b58babba.jpeg' }
Creative URL: http://s3.amazonaws.com/beta-adv-cdn/jpeg_ads/c62cf4b9-4d86-4613-be15-b6c3b58babba.jpeg
File Basename: c62cf4b9-4d86-4613-be15-b6c3b58babba.jpeg
Request: [object Object]
ERROR! Could not get base64 content of tmp file.
{ 'Banner Name': '...,
'Banner Size': '728x90',
'Banner URL':
'http://s3.amazonaws.com/beta-adv-cdn/jpeg_ads/95714da4-0085-4c0c-ba73-0346197c91db.jpeg' }
Creative URL: http://s3.amazonaws.com/beta-adv-cdn/jpeg_ads/95714da4-0085-4c0c-ba73-0346197c91db.jpeg
File Basename: 95714da4-0085-4c0c-ba73-0346197c91db.jpeg
Request: [object Object]
ERROR! Could not get base64 content of tmp file.
CSV file successfully processed
200 POST https://graph.facebook.com/v10.0/...
Uploaded 58748e44-83c7-4283-b090-36ce8dd8070b.jpeg successfully.
200 POST https://graph.facebook.com/v10.0/...
Uploaded dc5e9dcc-c6ab-4cbe-a334-814b4af4c4fa.jpeg successfully.

在Facebook Node.JS SDK上似乎没有太多文档,在Stack Exchange上关于它的问题更少,所以任何帮助都将不胜感激。

我不太确定你在这里期待什么。首先,console.log('after start ' + startPromise);是死码;它在返回语句之后。其次,您正在启动一个异步匿名函数而不等待它,然后调用process.exit(1);

您也应该等待自调用函数创建的Promise来解决和处理拒绝。大致如下:

console.log('before start');
const startPromise = start()
.then(data => {
for (var row of data) {
console.log(row);

const creative_url = row.creative_url;
const fileBasename = path.basename(creative_url);
const file = fs.createWriteStream("/tmp/" + fileBasename);
const request = http.get(creative_url, function(response) {
response.pipe(file);
});
// https://developers.facebook.com/docs/marketing-api/reference/ad-account/adimages/
if (uploadType == "image") {
let content = fs.readFileSync("/tmp/" + fileBasename).toString('base64');
// Upload image
}
}
})
.catch(e => {
console.log(e);
});

编辑:正如@Tomalak在评论中指出的那样,根本不需要异步包装器函数。

最新更新