Estoy trabajando en algo que toma un PDF de mi Google Cloud Storage. La URL del PDF se encuentra en la entrada de MongoDB. En realidad, bajar el archivo funciona bien. Pero tengo un problema con conseguir que se envíe al cliente. Creo que el problema radica en que los datos que está leyendo de alguna manera terminan vacíos, por lo que asumo que tiene que ver con asincronismo, devoluciones de llamada, promesas o algo por el estilo. Sin embargo, no puedo averiguar dónde está ocurriendo el error. Cualquier consejo sobre lo que me estoy perdiendo sería muy apreciado. Aquí está el código principal:
router.get('/:slug', async function(req, res) { const slug = req.params.slug; let fullPath; const find = await Entry.findOne({slug: slug}, (err, data) => { if(err) { res.render('error.ejs'); } else { console.log(data.fileURL); getFile(data.fileURL).then(async (fileName) => { fullPath = dir.APP_DIR + fileName; console.log(fullPath); fs.readFile(fullPath, async (err, data) => { if(err || data.length == 0) { res.render('error.ejs', {session: req.session}); } else { console.log('Full path is: ' + fullPath); console.log(data.length); res.writeHead(200, { 'Content-Type': 'application/pdf', 'Content-disposition': 'inline', 'Content-Length': data.length }).end(Buffer.from(data, 'binary')); } }) }); } }); });La función getFile se exporta y es la siguiente:
async function getFile(fileURL) { const url = fileURL; console.log(url); const newFileName = '/pdf_files/' + genID() + '.pdf'; const file = fs.createWriteStream('.' + newFileName); https.get(url, function(response) { response.pipe(file); file.on('finish', function() { file.close(); }); }); return newFileName; }Entonces, en teoría, su código parece estar bien. Tengo algunas optimizaciones de legibilidad para que el flujo de datos parezca un poco más claro. Estos cambios se reflejan a continuación.
Obviamente, no sé si esto solucionará su problema, pero me parece que tiene una condición de carrera en su función getFile(). Devuelve el nuevo nombre de archivo, pero no está esperando a que se escriba realmente el archivo. Luego, inmediatamente intenta leerlo, lo que probablemente no funcionará. La escritura es asíncrona y, por lo tanto, es posible que no se realice.
Aquí están mis cambios propuestos:
Su código principal:
router.get('/:slug', async function (req, res) { const slug = req.params.slug; let fullPath; try { const data = await Entry.findOne({slug}); console.log(data.fileURL); fileName = await getFile(data.fileURL); fullPath = dir.APP_DIR + fileName; console.log(fullPath); fs.readFile(fullPath, async (err, data) => { if (err || data.length == 0) { res.render('error.ejs', { session: req.session }); } else { console.log('Full path is: ' + fullPath); console.log(data.length); res.writeHead(200, { 'Content-Type': 'application/pdf', 'Content-disposition': 'inline', 'Content-Length': data.length }).end(Buffer.from(data, 'binary')); } }); } catch (e) { console.error(e); res.render('error.ejs'); } });Su método de obtención de archivos
async function getFile(fileURL) { const url = fileURL; console.log(url); const newFileName = '/pdf_files/' + genID() + '.pdf'; const file = fs.createWriteStream('.' + newFileName); // this await changes it all. This way you are waiting for the http request to be done. Then you return the name. await https.get(url, function (response) { response.pipe(file); file.on('finish', function () { file.close(); }); }); return newFileName; }Déjame saber si eso ayuda :)
Debe esperar a que se complete la escritura del archivo antes de regresar (usando una Promesa para ajustar las funciones que tienen una devolución de llamada como el código a continuación), no está esperando, por lo que en el siguiente flujo, el contenido del archivo puede estar vacío.
async function getFile(fileURL) { return new Promise( (resolve, reject) => { const url = fileURL; console.log(url); const newFileName = '/pdf_files/' + genID() + '.pdf'; const file = fs.createWriteStream('.' + newFileName); https.get(url, function (response) { file.on('finish', function () { file.close(); resolve(newFileName); }); response.pipe(file); }); }); }