- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用“exceljs”库。它在我的本地节点服务器上运行得很好。现在,我尝试使用 Firebase Functions 将 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)
});
在函数控制台上,日志显示 Excel 文件已成功创建。但是上传的时候,出现了一个很奇怪的错误:
我做错了什么?我感谢任何帮助。
谢谢!
最佳答案
您收到的错误消息来自于尝试获取不存在文件的签名 URL。
当您调用 bucket.upload(filename,metadata)
时,您将上传文件 /tmp/Relatorio.xlsx
,该文件会在您的存储桶中创建一个名为 Relatorio.xlsx
。在下一行中,您调用 bucket.file(filename);
,它错误地将自身与 /tmp/Relatorio.xlsx
而不是 Relatorio.xlsx
关联起来.
要解决此问题,您应该使用从 bucket.upload()
解析的 File
对象,而不是自己创建它:
bucket.upload(filename, metadata)
.then((file) => file.getSignedURL())
.then((url) => {
console.log('Salvar URL' + url)
})
您的代码还包含许多不必要的 new Promise((resolve,reject) => { ... })
调用。这称为 Promise 构造函数反模式,其中大多数可以通过正确链接 Promise 来删除。这个blog post是一个关于 Promise 以及如何正确使用它们的很好的速成类(class)。
关于函数的源代码,由于函数的 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);
});
});
关于javascript - 将 Excel 上传到 Google Cloud Storage 时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60291181/
我正在使用 node.js 和 mocha 单元测试,并且希望能够通过 npm 运行测试命令。当我在测试文件夹中运行 Mocha 测试时,测试运行成功。但是,当我运行 npm test 时,测试给出了
我的文本区域中有这些标签 ..... 我正在尝试使用 replaceAll() String 方法替换它们 text.replaceAll("", ""); text.replaceAll("", "
早上好,我是 ZXing 的新手,当我运行我的应用程序时出现以下错误: 异常Ljava/lang/NoClassDefFoundError;初始化 ICOM/google/zxing/client/a
我正在制作一些哈希函数。 它的源代码是... #include #include #include int m_hash(char *input, size_t in_length, char
我正在尝试使用 Spritekit 在 Swift 中编写游戏。目的是带着他的角色迎面而来的矩形逃跑。现在我在 SKPhysicsContactDelegate (didBegin ()) 方法中犯了
我正在尝试创建一个用于导入 CSV 文件的按钮,但出现此错误: actionPerformed(java.awt.event.ActionEvent) in cannot implement
请看下面的代码 public List getNames() { List names = new ArrayList(); try { createConnection(); Sta
我正在尝试添加一个事件以在“dealsArchive”表中创建一个条目,然后从“deals”表中删除该条目。它需要在特定时间执行。 这是我正在尝试使用的: DELIMITER $$ CREATE EV
我试图将两个存储过程的表结果存储到 phpmyadmin 例程窗口中的单个表中,这给了我 mariadb 语法错误。单独调用存储过程给出了结果。 存储过程代码 BEGIN CREATE TABLE t
我想在 videoview 中加载视频之前有一个进度条。但是我收到以下错误。我还添加了所有必要的导入。 我在 ANDROID 中使用 AIDE 这是我的代码 public class MainActi
我已经使用了 AsyncTask,但我不明白为什么在我的设备 (OS 4.0) 上测试时仍然出现错误。我的 apk 构建于 2.3.3 中。我想我把代码弄错了,但我不知道我的错误在哪里。任何人都请帮助
我在测试 friend 网站的安全性时,通过在 URL 末尾添加 ' 发现了 SQL 注入(inject)漏洞该网站是用zend框架构建的我遇到的问题是 MySQL -- 中的注释语法不起作用,因此页
我正在尝试使用堆栈溢出答案之一的交互式信息窗口。 链接如下: interactive infowindow 但是我在代码中使用 getMap() 时遇到错误。虽然我尝试使用 getMapAsync 但
当我编译以下代码时出现错误: The method addMouseListener(Player) is undefined for the type Player 代码: import java.
我是 Android 开发的初学者。我正在开发一个接收 MySql 数据然后将其保存在 SQLite 中的应用程序。 我将 Json 用于同步状态,以便我可以将未同步数据的数量显示为要同步的待处理数据
(这里是Hello world级别的自动化测试人员) 我正在尝试下载一个文件并将其重命名以便于查找。我收到一个错误....这是代码 @Test public void allDownload(
我只是在写另一个程序。并使用: while (cin) words.push_back(s); words是string的vector,s是string。 我的 RAM 使用量在 4 或 5
我是 AngularJS 的新手,我遇到了一个问题。我有一个带有提交按钮的页面,当我单击提交模式时必须打开并且来自 URL 的数据必须存在于模式中。现在,模式打开但它是空的并且没有从 URL 获取数据
我正在尝试读取一个文件(它可以包含任意数量的随机数字,但不会超过 500 个)并将其放入一个数组中。 稍后我将需要使用数组来做很多事情。 但到目前为止,这一小段代码给了我 no match for o
有些人在使用 make 命令进行编译时遇到了问题,所以我想我应该在这里尝试一下,我已经在以下操作系统的 ubuntu 32 位和挤压 64 位上尝试过 我克隆了 git 项目 https://gith
我是一名优秀的程序员,十分优秀!