Сообщение об ошибке появляется при попытке получить подписанный URL-адрес несуществующего файла.
Когда вы звоните bucket.upload(filename, metadata)
, вы загружаете файл /tmp/Relatorio.xlsx
, который создает файл в вашем ведре под названием Relatorio.xlsx
. На следующей строке вы вызываете bucket.file(filename);
, который неправильно ассоциируется с /tmp/Relatorio.xlsx
вместо Relatorio.xlsx
.
Чтобы исправить это, вы должны использовать объект File
, который разрешен из bucket.upload()
вместо создавая его самостоятельно:
bucket.upload(filename, metadata)
.then((file) => file.getSignedURL())
.then((url) => {
console.log('Salvar URL' + url)
})
Другие заметки и исправления
Ваш код также содержит множество ненужных new Promise((resolve, reject) => { ... })
вызовов. Это называется анти-шаблон конструктора Promise , и большинство из них можно удалить, правильно связав Обещания. Этот пост в блоге - хороший курс sh об обещаниях и их правильном использовании.
Относительно исходного кода вашей функции, поскольку файл index.js
для функций будет содержать несколько функций определения, вы не должны определять переменные в верхней части вашего index.js
файла, если они не являются общими для всех ваших функций, и они не сохраняют состояния в случае, если функция вызывается несколько раз. Это особенно важно, когда речь идет о ресурсах ввода-вывода или ресурсоемких ресурсах, таких как файлы.
С вашим текущим кодом, если функция relator ios вызывается дважды за короткий период, сохраненный файл будет содержать и старые данные из первого вызова, и новые данные из текущего вызова, приводящие как к неверному файлу, так и к потенциальной утечке памяти.
Удаление вызовов с чрезмерным обещанием и создание так, чтобы ваш код exceljs
мог перезапустить без повреждения каких-либо данных в следующем файле index.js
:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
// 'exceljs' is required on-demand in MyExcelSheetHelper
admin.initializeApp();
/* HELPER CLASS */
/**
* A helper class used to create reuseable functions that won't
* conflict with each other
*/
class MyExcelSheetHelper {
constructor() {
const ExcelJS = require('exceljs');
this.workbook = new ExcelJS.Workbook();
this.worksheet = this.workbook.addWorksheet('Relatório Consolidado');
this.worksheet.columns = [
{ header: 'Empresa', key: 'empresa', width: 25 },
{ header: 'Data criação', key: 'data_criacao', width: 25 },
{ header: 'Responsável agendamento', key: 'agendador', width: 25 },
{ header: 'Colaborador', key: 'colaborador', width: 25 },
{ header: 'Endereço', key: 'endereco', width: 25 },
{ header: 'CPF', key: 'cpf', width: 25 },
{ header: 'CTPS', key: 'ctps', width: 25 },
{ header: 'Função', key: 'funcao', width: 25 },
{ header: 'Data agendado', key: 'nome_subtipo_produto', width: 25 },
{ header: 'Data atendimento médico', key: 'nome_subtipo_produto', width: 25 },
{ header: 'Data inicio atendimento', key: 'nome_subtipo_produto', width: 25 },
{ header: 'Data inicio exames', key: 'nome_subtipo_produto', width: 25 },
{ header: 'Tipo de exame', key: 'valor_produto', width: 25 },
{ header: 'Exames realizados', key: 'valor_produto', width: 25 },
{ header: 'Status atendimento', key: 'tipoPagamento', width: 25 },
{ header: 'Status exames', key: 'centroCustoStr', width: 25 }
];
}
/**
* Streams this workbook to Cloud Storage
* @param storageFilepath - the relative path where the file is uploaded to Cloud Storage
* @returns the signed URL for the file
*/
salva(storageFilepath) {
if (!storageFilepath) {
return Promise.reject(new Error('storageFilepath is required'));
}
const bucket = admin.storage().bucket();
const storageFile = bucket.file(storageFilepath);
const uploadFilePromise = new Promise((resolve, reject) => {
try {
const stream = storageFile.createWriteStream({
contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
stream.on('finish', () => {
resolve();
});
stream.on('error', error => {
reject(error);
});
this.workbook.xlsx.write(stream)
.then(() => {
stream.end();
});
} catch (e) { // catches errors from createWriteStream
reject(e);
}
})
return uploadFilePromise
.then(() => {
var CONFIG = {
action: 'read',
expires: '03-01-2500',
};
bucket.file(storageFilepath).getSignedUrl(CONFIG)
.then((signedUrl) => {
return signedUrl
})
})
}
}
/* FUNCTIONS CODE */
function criaRelatorioAgendamentos(path, querySnapshot) {
const excelFileHelper = new MyExcelSheetHelper();
const worksheet = excelFile.worksheet;
// this forEach loop is synchronous, so no Promises are needed here
querySnapshot.forEach(entrySnapshot => {
const data = entrySnapshot.val();
worksheet.addRow({
id: 1,
empresa: data.agendador.company,
data_criacao: data.dataCriacao,
agendador: data.agendador.nome,
colaborador: data.colaborador.nome,
cpf: data.colaborador.cpf,
ctps: data.colaborador.ctps,
funcao: data.colaborador.funcao,
data_agendado: data.data,
data_atendimento_medico: data.dataAtendimento,
data_inicio_atendimento: data.dataInicio,
data_inicio_exames: data.dataInicioExames,
tipo_exame: data.tipoExame,
exames: data.exames[0].nome,
status_atendimento: data.status,
status_exames: data.statusExames
});
});
return excelFileHelper.salva(path + '/Relatorio.xlsx');
}
exports.relatorios = functions.database.ref('/relatorios/{state}/{year}/{month}/{relatoriosId}')
.onWrite((change, context) => {
// Verificar relatorio agendamentos
const snapshot = change.after;
const data = snapshot.val();
const dataInicial = data.dataInicial;
const year = moment(dataInicial).format('YYYY');
const month = moment(dataInicial).format('MM');
const state = 'DF';
const path = "/agendamentos/" + state + "/" + year + "/" + month;
return admin.database().ref(path).once('value')
.then(valores => {
return criaRelatorioAgendamentos(path, valores);
});
});