将 Excel 上传到 Google Cloud Storage 时出错

迭戈开发者

我正在使用“exceljs”库。它在我的本地节点服务器上运行良好。现在我正在尝试使用 Firebase 函数将 excel 文件上传到 Google Cloud 存储。

这是我正在使用的整个代码:

'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const ExcelJS = require('exceljs');

admin.initializeApp();

var workbook = new ExcelJS.Workbook();
var worksheet = workbook.addWorksheet('Relatório Consolidado');



function startExcel(){

  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 }
  ];        
}

function salvaExcel(){

  return new Promise(function(resolve, reject){

      let filename = `/tmp/Relatorio.xlsx`
      let bucketName = 'gs://xxx.appspot.com/Relatorios'
      const bucket = admin.storage().bucket(bucketName);      

      workbook.xlsx.writeFile(filename)
      .then(() => {

      console.log('Excel criado com sucesso! Enviando upload do arquivo: ' + filename)          

        const metadata = {
          contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        };

        bucket.upload(filename, metadata)

        .then(() => {
          const theFile = bucket.file(filename);
          theFile.getSignedURL(signedUrlOptions)

          .then((signedUrl) => {
            resolve(signedUrl)
          });

        })
        .catch((error) => {
          reject('Erro ao realizar upload: ' + error)
        })                      
      })  

      .catch((error) => {
        reject('Erro ao realizar upload: ' + error)
      })         

  })    
}

startExcel()


/**********************************
 * Relatórios
 ********************************/

function relatorios(change, context){

  return new Promise((resolve, reject) => {

    const snapshot = change.after
    const data = snapshot.val()  

    verificaRelatorioAgendamentos(change)

    .then(() => {
      resolve()

    })

    .catch((error => {                
      reject(error)

    }))

  })           
}


function verificaRelatorioAgendamentos(change, context){

  return new Promise((resolve, reject) => {

    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'    
    let path = "/agendamentos/" + state + "/" +  year + "/" + month

    const relatorios = admin.database().ref(path).once('value');

    return Promise.all([relatorios])

      .then(results => {                

        let valores = results[0]
        criaRelatorioAgendamentos(valores)

        .then(() => {
          resolve()

        })

        .catch((error => {          
          reject(error)

        }))

    })

  })       

}


function criaRelatorioAgendamentos(results){

  return new Promise((resolve, reject) => {

    let promises = []

    results.forEach(element => {

      let promise = new Promise(function(resolveExcel){ 

        let data = element.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

        })

        resolveExcel()

      })      

      promises.push(promise)

    })

    Promise.all(promises)

      .then(() => {          
        salvaExcel()

        .then((url) => {

          console.log('Salvar URL' + url) 

          resolve(url)

        })

        .catch((error => {
          reject(error)

        }))


    })


  })       

}


exports.relatorios = functions.database.ref('/relatorios/{state}/{year}/{month}/{relatoriosId}')
    .onWrite((change, context) => {      
      return relatorios(change, context)
});

在 Functions 控制台上,日志显示 excel 文件已成功创建。但是在上传的时候,弹出一个很奇怪的错误:

在此处输入图片说明

我做错了什么?我很感激任何帮助。

谢谢!

编码员

您收到的错误消息来自尝试获取不存在文件的签名 URL。

当您调用时bucket.upload(filename, metadata),您正在上传文件/tmp/Relatorio.xlsx,这会在您的存储桶中创建一个名为Relatorio.xlsx. 在下一行,您调用bucket.file(filename);which 错误地将其与/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 构造函数反模式,其中大部分可以通过正确链接Promise来删除。这篇博文是关于 Promise 以及如何正确使用它们的很好的速成课程。

关于函数的源代码,由于index.js函数文件将包含多个函数定义,因此不应在index.js文件顶部定义变量,除非它们被所有函数共享,并且在函数被多次调用时它们是无状态的。这在处理 I/O 或内存密集型资源(如文件)时尤为重要。

使用您当前的代码,如果 relatorios 函数在短时间内被调用两次,则保存的文件将包含来自第一次调用的旧数据和来自当前调用的新数据,从而导致文件无效和潜在的内存泄漏。

删除过多的 promise 调用并进行调用,以便您的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);
      });
});

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

将 xml 文件上传到 Google Cloud Storage 时出错

将图像从Google Cloud Function上传到Cloud Storage

使用Flask将文件上传到Google Cloud Storage

Alamofire无法将文件上传到Google Cloud Storage

将文件上传到Google Cloud Storage:500后端错误

自动将数据上传到 Google Cloud Storage 和 BigQuery

将.raw文件上传到Google Cloud Storage

使用 Codeigniter 将文件上传到 Google Cloud Storage

使用 For 循环将多个文件上传到 Google Cloud Storage

在node.js中将文件上传到Google Cloud Storage时出错

使用“gsutil”从 Cloud Storage 存储分区上传到 Google Colab 时出错

上传到Google Cloud Storage时获得403 SignatureDoesNotMatch?

将多个 Pandas 数据框作为包含多个工作表的单个 Excel 文件上传到 Google Cloud Storage

403调用fork()后,使用Python SDK将数据上传到Google Storage时出错

使用Google App Engine(Python)将文件上传到Google Cloud Storage

使用Google App Engine将大文件上传到Google Cloud Storage

将文件上传到Google Cloud Storage而不在本地下载文件?

使用JSON API将文件上传到Google Cloud Storage,错误401未经授权

将文件直接上传到Google Cloud Storage for GAE应用

使用PHP和Ajax将文件上传到Google Cloud Storage Bucket

将Python文件上传到Google Cloud Storage Bucket返回管道中断错误

使用Node JS将图像从本地目录上传到Google Cloud Storage

如何从Node中的图像URL将图像上传到Google Cloud Storage?

如何使用JSON API和CloseableHttpClient将文件上传到Google Cloud Storage存储桶?

无法将XCTest软件包上传到Google Cloud Storage for Firebase测试实验室

如何通过Python GAE将视频上传到Google Cloud Storage?

使用 Node.js 将文件上传到 Google Cloud Storage(已声明“bucketName”)

使用Python将文件上传到Google Cloud Storage签名的URL

将python对象上传到Google Cloud Storage,而不保存到文件中