经过大量的研究和测试,我被困在了瀑布中:
async.waterfall([
//first 5 functions are going fine and passing expected vars to the next function the function below is the pb
// eg adSetId var comes just fine from previous function
function(imgName,adsetId,callback) {
fs.readFile('./shopifyImages/'+imgName , 'base64' ,function(err,imgData) {
callback(null,imgData,adsetId);
});
}
]);
imgData
始终返回空值。
函数没有错误及其工作正常的瀑布(fs.readFile
按预期工作),有些console.log
也提供空imgData
。
完整代码
async.waterfall([
//GET AD ACCOUNT FOR USER
function(callback) {
var sqlAd_act = vsprintf('select * from fbTokens where userId=%s', [roomIDCook.userId]);
connection.query(sqlAd_act).then(function(rows) {
//console.log('rows '+rows);
callback(null, rows[0].ad_act);
});
},
//SET CAMPAIGN
function(ad_act, callback) {
fb.api('/act_' + ad_act + '/campaigns', 'post', {
'status': 'PAUSED',
'name': 'GENIE 2'
}, function(data) {
//console.log('data: ' + util.inspect(data))
callback(null, data.id, ad_act);
});
},
//SET ADSET
function(campaignId, ad_act, callback) {
console.log('ad account ' + ad_act);
console.log('campaignId ' + campaignId);
fb.api('/act_' + ad_act + '/adsets', 'post', {
'daily_budget': 500,
'start_time': '2017-02-18T20:11:25+0000',
'end_time': '2017-03-25T20:11:25+0000',
'name': 'new adset',
'optimization_goal': 'LINK_CLICKS',
'objective': 'LINK_CLICKS',
'campaign_id': campaignId,
'status': 'PAUSED',
'billing_event': 'LINK_CLICKS',
'bid_amount': 2,
'targeting': {
"geo_locations": {
"countries": ["US"]
},
"publisher_platforms": ["facebook"]
}
},
function(res) {
if (!res || res.error) {
console.log(!res ? 'error occurred' : util.inspect(res.error));
return;
}
//console.log(util.inspect(res));
callback(null, res.id)
});
},
// CREATE THE AD FOR THE ADSET
function(adsetId, callback) {
// set img
var imgUri = 'https://cdn.shopify.com/s/files/1/1336/5343/products/9_b75df43c-1660-4aeb-89b7-418df1a9853c_1024x1024.jpg?v=1466985077';
var DOWNLOAD_DIR = 'shopifyImages/';
var imgName = 'TEST2.jpg';
console.log('adsetId ' + adsetId);
// download img
request.head(imgUri, function(err, res, body) {
console.log('content-type:', res.headers['content-type']);
console.log('content-length:', res.headers['content-length']);
request(imgUri).pipe(fs.createWriteStream(DOWNLOAD_DIR + imgName));
console.log('adsetId ' + adsetId);
callback(null, imgName, adsetId);
});
// read img for facebook
},
function(imgName, adsetId, callback) {
fs.readFile('./shopifyImages/' + imgName, 'base64', function(err, imgData) {
console.log(util.inspect(err));
console.log(util.inspect(imgData));
callback(null, imgData, adsetId);
});
},
function(imgData, adsetId, callback) {
console.log(imgData);
console.log('adsetId ' + adsetId);
}
], function(error, c) {
console.log(c);
});
答案 0 :(得分:1)
由于流是异步的,因此存在计时问题。你这样做:
request(imgUri).pipe(fs.createWriteStream(DOWNLOAD_DIR + imgName));
callback(null, imgName, adsetId);
但是,在调用callback()
之前,您不是在等待流完成,而是继续下一步,您希望imgName
文件已经存在。因此,您正在尝试在完成编写之前读取新创建的文件。
您需要为该流注册一个事件处理程序,以便您知道该流何时完成编写,然后才能调用callback(null, imgName, adsetId)
进行下一步。
我不是一个流媒体高手,所以可能有更优雅的方式来做到这一点,但这是一个解决方案:
// CREATE THE AD FOR THE ADSET
function(adsetId, callback) {
// set img
var imgUri = 'https://cdn.shopify.com/s/files/1/1336/5343/products/9_b75df43c-1660-4aeb-89b7-418df1a9853c_1024x1024.jpg?v=1466985077';
var DOWNLOAD_DIR = 'shopifyImages/';
var imgName = 'TEST2.jpg';
console.log('adsetId ' + adsetId);
// download img
request.head(imgUri, function(err, res, body) {
console.log('content-type:', res.headers['content-type']);
console.log('content-length:', res.headers['content-length']);
console.log('adsetId ' + adsetId);
var outputStream = fs.createWriteStream(DOWNLOAD_DIR + imgName);
outputStream.on('finish', function() {
callback(null, imgName, adsetId);
});
outputStream.on('error', function(err) {
callback(err);
});
request(imgUri).pipe(outputStream);
});
仅供参考,在一步中将数据写入文件然后在下一步中将其读回内存中,概念上很奇怪。效率很低。为什么不在第一次进入内存时读取它,或者如果你仍然想要它在文件中,然后将其写入文件,但在内存中保留你写的数据?