У меня есть рабочий код Nodejs, однако библиотека child_process ведет себя странно, мне просто интересно, как работает эта библиотека.
Мой код пытается загрузить SSL-сертификаты с S3, а затем создать два новых файла на основе существующих, используя библиотеку child_process.
const AWS = require('aws-sdk');
const fs = require('fs');
const child_process = require("child_process");
const exec = require('child_process').exec;
var s3 = new AWS.S3();
var filePath = '../Desktop/Certs/'
var bucketName = 'neb.certificates' //replace example bucket with your s3 bucket name
var params = {
Bucket: bucketName,
Prefix: 'dev/jenkins.secure.care/',
};
s3.listObjectsV2(params, function(err, data) {
if (err) console.info(err, err.stack); // an error occurred
else {
// console.info(data.Contents)
var len = data.Contents.length
for(var i=0; i<len; i++){
var key = data.Contents[i]["Key"]
var newPath = filePath.concat(key.substring(31))
const downloadFile = (newPath, bucketName, key) => {
//construct getParam
var getParams = {
Bucket: bucketName,
Key: key
};
s3.getObject(getParams, (err, data) => {
if (err) console.error(err)
fs.writeFileSync(newPath, data.Body.toString())
// console.info(`${newPath} has been created!`)
})
}
downloadFile(newPath, bucketName, key)
}
}
});
exec('mv ../Desktop/Certs/cert.pem ../Desktop/Certs/jenkins.crt', (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
console.info(stdout);
});
exec('mv ../Desktop/Certs/privkey.pem ../Desktop/Certs/jenkins.key', (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
console.info(stdout);
});
Поэтому, когда я запускаю код в первый раз, он загружает только сертификаты с S3 в локальную папку, а не создает два других файла. Затем мне нужно запустить его во второй раз, чтобы создать дополнительные файлы.
Однако я просто хочу запустить его один раз, и в нем есть все, что я ожидаю.
Я добавил код, который будет спать 5 секунд, а затем создавать дополнительные файлы, но это не решило мою проблему, а это значит, что я все еще запускаю код дважды, чтобы получить все.
child_process.execSync("sleep 5");
Пожалуйста помогите





Похоже, проблема здесь в асинхронном выполнении вашего кода. В этом примере вот что происходит:
s3.listObjectsV2() и когда закончите, активируйте обратный вызов (но не сейчас, в будущем)exec() и когда закончите, активировать обратный вызов (но не сейчас, в будущем)exec() и когда закончите, активируйте обратный вызов (но не сейчас, в будущем)И эти три шага запускаются сразу, один за другим. И у каждого из них есть свой обратный вызов, который запускает в будущем. Хорошо, но когда будущее? - точно! Вы не знаете. В вашем случае, вероятно, эти два обратных вызова в exec() запускаются до обратного вызова из s3, и поэтому он не работает.
Решение здесь состоит в том, чтобы убедиться, что эти exec() запускаются после s3.listObjects. Таким образом, у вас есть два варианта: сначала сделать обещание из s3, например: s3.listObjectsV2(params).promise() и await для этого, затем в .then((data) => {}) у вас есть data, а в .catch((error) => {}) у вас есть error. Или вы можете просто поместить эти exec() в обратный вызов вызова s3.
Ваш код должен выглядеть так в соответствии с решением 2 (из комментариев):
const AWS = require('aws-sdk');
const fs = require('fs');
const child_process = require("child_process");
const exec = require('child_process').exec;
var s3 = new AWS.S3();
var filePath = '../Desktop/Certs/'
var bucketName = 'neb.certificates' //replace example bucket with your s3 bucket name
var params = {
Bucket: bucketName,
Prefix: 'dev/jenkins.secure.care/',
};
s3.listObjectsV2(params, async function(err, data) {
if (err) console.info(err, err.stack); // an error occurred
else {
// console.info(data.Contents)
var len = data.Contents.length
for(var i=0; i<len; i++){
var key = data.Contents[i]["Key"]
var newPath = filePath.concat(key.substring(31))
const downloadFile = (newPath, bucketName, key) => {
//construct getParam
var getParams = {
Bucket: bucketName,
Key: key
};
return s3.getObject(getParams).promise();
};
const downloadData = await downloadFile(newPath, bucketName, key).catch(console.error);
fs.writeFileSync(newPath, downloadData.Body.toString());
console.info(newPath, 'created');
}
//rename files
console.info('renaming first cert.pem');
exec('mv ../Desktop/Certs/cert.pem ../Desktop/Certs/jenkins.crt', (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
console.info(stdout);
});
console.info('renaming second privkey.pem');
exec('mv ../Desktop/Certs/privkey.pem ../Desktop/Certs/jenkins.key', (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
console.info(stdout);
});
}
});
Вам нужно поместить эти exec под этой строкой: fs.writeFileSync(newPath, data.Body.toString()), потому что здесь вы закончили загрузку файлов с s3 и можете работать с ними (например, переименовывать e.x.)
Мне все еще трудно реализовать обратный вызов в моем коде :(
Я добавляю callback в качестве необязательного аргумента в функцию, которая выглядит так const downloadFile = (newPath, bucketName, key, callback) Я понятия не имею, как поместить 2 обратных вызова в вызов
Я только что попробовал ваше обновленное решение, оно все еще не работает в моем случае :(
Каков точный результат одного запуска обновленного кода? Вы можете опубликовать это?
Давайте продолжить обсуждение в чате.
Спасибо за решение, я очень новичок в Nodejs, и я думаю, что предпочитаю помещать эти
exec()в функцию обратного вызова, вопрос в том, где мне нужно поместить функцию обратного вызова. Под этой строкойdownloadFile(newPath, bucketName, key)хорошая идея?