Я загружаю файл в Google Cloud. По завершении я хочу вернуть URL-адрес файла и некоторые метаданные. Я пытаюсь сделать это так:
blobStream.on('finish', () => {
var fileSize;
var fileCreated;
fileUpload.getMetadata()
.then(metadata => {
const mObj = metadata[0];
fileSize = mObj.size;
fileCreated = mObj.timeCreated;
});
fileUpload.getSignedUrl({
action: 'read',
expires: '03-09-2491'
}).then(signedUrl => {
let response = {};
response.name = newFileName;
response.size = fileSize;
response.created = fileCreated;
response.url = signedUrl[0];
resolve(response);
}).catch(error => {
reject(error);
});
});
Проблема в том, что метод getSignedUrl всегда запускается первым, а «fileSize» + «fileCreated» никогда не заполняются.
Я считаю, что мне нужно связать события, чтобы сначала выполнялось getMetadata, но я относительно новичок в Node (и обещаниях) и мог бы использовать некоторые предложения.
РЕДАКТИРОВАТЬ ОТВЕТ НА БЕРГИ: (Метод ниже работает)
uploadFileToStorage(file) {
let utils = new Utils();
let newFileName = `${utils._guid()}-${Date.now()}`;
let bucket = admin.storage().bucket();
let filePath = url.format('audioFiles/' + newFileName);
let fileUpload = bucket.file(filePath);
let prom = new Promise(function(resolve, reject) {
if (!file) {
reject('No file provided');
return;
}
const blobStream = fileUpload.createWriteStream({
metadata: {
contentType: file.mimetype
}
});
blobStream.on('error', (error) => {
reject('Something is wrong! Unable to upload at the moment.');
return;
});
blobStream.on('finish', resolve);
blobStream.end(file.buffer);
}).then(() => {
return Promise.all([
fileUpload.getMetadata(),
fileUpload.getSignedUrl({
action: 'read',
expires: '03-09-2491'
}),
]);
}).then(([metadata, signedUrl]) => {
const fileSize = metadata[0].size;
const fileCreated = metadata[0].timeCreated;
const response = {
name: newFileName,
size: fileSize,
created: fileCreated,
url: signedUrl[0],
};
return response;
});
return prom;
}
Один из вариантов - переместить getSignedUrl туда, где вы получаете свои метаданные. Или добавьте еще один .then () после getMetadata (). Then ().





Не создавайте два независимых обещания и не используйте переменные внешней области, о которых вы никогда не знаете, когда они будут назначены. Вместо этого используйте Promise.all, чтобы дождаться нескольких обещаний:
new Promise(resolve => {
blobStream.on('finish', resolve);
}).then(() => {
return Promise.all([
fileUpload.getMetadata(),
fileUpload.getSignedUrl({
action: 'read',
expires: '03-09-2491'
}),
]);
}).then(([metadata, signedUrl]) => {
const fileSize = metadata[0].size;
const fileCreated = metadata[0].timeCreated;
const response = {
name: newFileName,
size: fileSize,
created: fileCreated,
url: signedUrl[0],
});
return response;
});
Также избегайте используя дальнейшие обещания внутри конструктора Promise!
Спасибо, Берги. Я опубликовал весь свой метод выше. В настоящее время я получаю сообщение «TypeError: невозможно прочитать свойство 'then' of undefined»
@Peter В условии if (!file) вы захотите return из функции после отклонения. Кроме того, полная функция uploadFileToStorage не делает return результатом цепочки обещаний - поэтому при использовании uploadFileToStorage().then(…) она производит undefined.
Большое спасибо, @Bergi. Я очень ценю это. Я проработал это и опубликовал окончательную версию в своем исходном посте. Огромное спасибо!
Если вам нужно больше, дайте мне знать, как я могу улучшить этот вопрос?