Generated docx is empty when downloaded - node.js

This is a Meteor-app. I need to generate a docx-file and download it.
I am testing it by running: localhost:3000/download.
Word file is generated, but it is totally empty.
Why? I would appreciate any advice!
This is my server-side code:
const officegen = require('officegen');
const fs = require('fs');
Meteor.startup(() => {
WebApp.connectHandlers.use('/download', function(req, res, next) {
const filename = 'test.docx';
let docx = officegen('docx')
// Create a new paragraph:
let pObj = docx.createP()
pObj.addText('Simple')
pObj.addText(' with color', { color: '000088' })
pObj.addText(' and back color.', { color: '00ffff', back: '000088' })
pObj = docx.createP()
pObj.addText(' you can do ')
pObj.addText('more cool ', { highlight: true }) // Highlight!
pObj.addText('stuff!', { highlight: 'darkGreen' }) // Different highlight color.
docx.putPageBreak()
pObj = docx.createP()
let out = fs.createWriteStream(filename);
res.writeHead(200, {
'Content-Disposition': `attachment;filename=${filename}`,
'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
res.end(docx.generate(out));
});
});

The problem you are facing is that docx.generate(out) is an async function: when calling res.end(docx.generate(out)) you end the request right now while you start generating the docx in the file test.docx. Hence the doc does not exist yet.
You should modify your code to send over the file directly like this:
res.writeHead(200, {
'Content-Disposition': `attachment;filename=${filename}`,
'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
docx.generate(res)
If you still need the file on the server side you can use another approach waiting for the file being generated (see here)

Related

Generate PDF with pdfkit

I'm changing the configuration of the website regarding the a pdf creation and download by the user. The idea here is that the pdf is created from data from the DB and it's not stored, but directly offered to the user for download.
The original website was using html-pdf, but I'm unable to install it successfully, as I always have issue with phantomjs, phantomjs-prebuilt, etc.
I tried using pdfkit, but i'm not successful, and since I'm not very experienced the problem certainly is something I'm not quite getting.
Can someone give me some pointers, please?
let express = require('express'),
router = express.Router(),
asyncMiddleware = require('../utils/asyncMiddleware'),
auth = require('../middleware/authentication'),
// pdf = require('html-pdf'),
blobStream = require('blob-stream'),
pdf = require('pdfkit'),
juice = require('juice'),
{User, Report} = require('../models');
router.get('/:reportId?', auth.isLoggedIn, asyncMiddleware( async(req, res) => {
let reqUser = req.user;
//get the report
let report = await Report.findById( req.params.reportId ).populate('ownerManagerId');
if( !report ) {
req.flash('error', req.__('flash.error.reportNotFound') );
return res.redirect('back');
}
//check if user on the request is the user of the report or is the group admin stored when the report was generated
if( !reqUser._id.equals(report.ownerId) && !reqUser._id.equals(report.ownerManagerId) && req.user.role !=="admin" ){
req.flash('error', req.__('flash.error.forbiddenAccess') );
return res.redirect('back');
}
//report data
let data = {
layout: 'pdf_layout',
styles: [],
scripts: [],
user: reqUser,
lang: req.lang,
pageTitle: req.__('pageTitle-calculator-report'),
reportOwnerManager: report.ownerManagerId,
reportDate: formatDateInReport( report.createdAt ),
calcData: report.inputs,
output: report.outputs
}
//add some helper functions to ejs views
res.locals.formatNum = formatNum;
res.locals.findInputCorr = findInputCorr;
res.render('calculadora/relatorioPdf', data, (err1, html)=>{
if(err1) return res.send(err1);
//inline all css on html
let inlineHtml = juice(html);
//set pdf options
var pdfOptions = {
width: "297mm",
height: "420mm", // allowed units: mm, cm, in, px
//format: "A4", // allowed units: A3, A4, A5, Legal, Letter, Tabloid
//orientation: "portrait",
border: {
top: "30mm", // default is 0, units: mm, cm, in, px
right: "20mm",
bottom: "0",
left: "20mm"
}
};
// //create the pdf and stream it to response
// pdf.create(inlineHtml, pdfOptions).toStream( (err2, stream)=>{
// if(err2) return res.send(err2);
// //set headers so the file is downloaded instead of shown in browser
// res.writeHead(200, {
// 'Content-Type': 'application/pdf',
// 'Content-disposition': `attachment; filename=relatorio-${report.id}.pdf`
// });
// //redirect stream to response
// stream.pipe(res);
// });
//set headers so the file is downloaded instead of shown in browser
const stream = res.write(200, {
'Content-Type': 'application/pdf',
'Content-disposition': `attachment; filename=relatorio-${report.id}.pdf`
});
buildPDF(
(chunk) => stream.write(chunk),
() => stream.end()
);
function buildPDF() {
const doc = new pdf();
doc.on('data', dataCallback);
doc.end('data', endCallback);
doc.pipe(res);
doc.end();
}
});//render
}))//get´´´

Questions regarding pdf to image conversion with Node JS

The plan is to create a pdf file (that only consists of a single page) then the user chooses whether to download as PDF or image. I have already written the code for generating the PDF and it is working fine as of now. The problem now is how to convert this to image. Is it possible to convert files without installing stuff like Ghostscript etc?
I am a complete noob, advice is greatly appreciated. (Recommendations on which libraries to use would also be helpful)
Code for generating the PDF
import PDFDocument from "pdfkit";
static async medicalPrescription(req, res) {
// Some code for generating the PDF contents...
filename = encodeURIComponent(filename) + '.pdf'
res.setHeader('Content-disposition', 'attachment; filename="' + filename + '"')
res.setHeader('Content-type', 'application/pdf')
const content = req.body.content
doc.y = 300
doc.text(content, 50, 50)
doc.pipe(res)
doc.end()
}
The client then receives the generated file and opens it in another tab.
React file that sends the request and opens the response
const handleSubmit = async (e) => {
// Some code for sending the pdf content from the user
fetch("http://localhost:5050/generate-rx", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: parsed
})
.then(async res => {
if (res.status === 200) {
const blob = await res.blob();
const file = new Blob(
[blob],
{type: 'application/pdf'}
);
const fileURL = URL.createObjectURL(file);
window.open(fileURL);
}
})
}
You can use pdf2pic. It can convert pdf to image.
import { fromPath } from "pdf2pic";
const options = {
density: 100,
saveFilename: "untitled",
savePath: "./images",
format: "png",
width: 600,
height: 600
};
const storeAsImage = fromPath("/path/to/pdf/sample.pdf", options);
const pageToConvertAsImage = 1;
storeAsImage(pageToConvertAsImage).then((resolve) => {
console.log("Page 1 is now converted as image");
console.log(resolve); // send resolve to user
});

Why my react front end does not want to download my file sent from my express back end?

hope you can help me on this one!
Here is the situation: I want to download a file from my React front end by sending a request to a certain endpoint on my express back end.
Here is my controller for this route.
I build a query, parse the results to generate a csv file and send back that file.
When I console log the response on the front end side, the data is there, it goes through; however, no dialog open allowing the client to download the file on local disk.
module.exports.downloadFile = async (req, res) => {
const sql = await buildQuery(req.query, 'members', connection)
// Select the wanted data from the database
connection.query(sql, (err, results, fields) => {
if (err) throw err;
// Convert the json into csv
try{
const csv = parse(results);
// Save the file on server
fs.writeFileSync(__dirname + '/export.csv', csv)
res.setHeader('Content-disposition', 'attachment; filename=export.csv');
res.download(__dirname + '/export.csv');
} catch (err){
console.error(err)
}
// Reply with the csv file
// Delete the file
})
}
Follow one of these functions as an example to your client side code:
Async:
export const download = (url, filename) => {
fetch(url, {
mode: 'no-cors'
/*
* ALTERNATIVE MODE {
mode: 'cors'
}
*
*/
}).then((transfer) => {
return transfer.blob(); // RETURN DATA TRANSFERED AS BLOB
}).then((bytes) => {
let elm = document.createElement('a'); // CREATE A LINK ELEMENT IN DOM
elm.href = URL.createObjectURL(bytes); // SET LINK ELEMENTS CONTENTS
elm.setAttribute('download', filename); // SET ELEMENT CREATED 'ATTRIBUTE' TO DOWNLOAD, FILENAME PARAM AUTOMATICALLY
elm.click(); // TRIGGER ELEMENT TO DOWNLOAD
elm.remove();
}).catch((error) => {
console.log(error); // OUTPUT ERRORS, SUCH AS CORS WHEN TESTING NON LOCALLY
})
}
Sync:
export const download = async (url, filename) => {
let response = await fetch(url, {
mode: 'no-cors'
/*
* ALTERNATIVE MODE {
mode: 'cors'
}
*
*/
});
try {
let data = await response.blob();
let elm = document.createElement('a'); // CREATE A LINK ELEMENT IN DOM
elm.href = URL.createObjectURL(data); // SET LINK ELEMENTS CONTENTS
elm.setAttribute('download', filename); // SET ELEMENT CREATED 'ATTRIBUTE' TO DOWNLOAD, FILENAME PARAM AUTOMATICALLY
elm.click(); // TRIGGER ELEMENT TO DOWNLOAD
elm.remove();
}
catch(err) {
console.log(err);
}
}

I've got an issue with ReactJS to download a file from Nodejs

I need some help on an issue. I try to download a file on my client ReactJs from my server Nodejs.
I've got into my server.js :
router.route("/download/:filesaveas").get(function(req, res) {
const fileLocation = "public/files/" + req.params.filesaveas;
const file = req.params.filesaveas;
res.download(fileLocation, file, (err) => {
if (err) console.log(err);
});
When I try to download directly from the server http://localhost:4000/cww/download/testfile.pdf, the download works, I don't have any error and the file is not corrupted.
On my client side, I've got a function downloadFile which is called by a button "onclick" action.
import download from 'downloadjs'
downloadFile = (filetodownload) => {
axios.get('http://localhost:4000/cww/download/'+filetodownload)
.then(res => {
var filename = "testfile.pdf"
download(res.data, scriptname, "text/plain");
});
}
When I click on the button. Something is downloaded but the file seems to be corrupted. Impossible to open... I think, I've got a problem with the response data from the server.
By doing a console.log(res.data), I can see a part of my content PDF but with some strange
characters (like encoding) but impossible to have a correct file downloaded.
Please thanks for your help.
if you want easiest option would be to open a new tab with that file's address which works only if route is public.
const newTab = false;
window.open('http://localhost:4000/cww/download/testfile.pdf',newTab ? '' : '_self' );
but you can do it without touching the file encoding or even use axios for this:
onClick() {
const fileName = 'testfile.pdf';
fetch('http://localhost:4000/cww/download/testfile.pdf', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
// Security Headers if needed
},
body: undefined,
})
.then((data) => {
return data.blob();
})
.then((data) => {
if (data.size === 0) {
throw new Error('File not found');
}
const fileURL = URL.createObjectURL(data);
const downloadLink = document.createElement('a');
downloadLink.href = fileURL;
downloadLink.download = fileName;
downloadLink.click();
})
.catch((err) => {
console.log(err);
// Error Action
});
}
Backend will simply stream the file to user.
I don't know much about res.download (might be better solution)
import * as fs from 'fs';
export async function getFile(request, response) {
const fileStream = fs.createReadStream('yourFileAddress', {});
fileStream.on('error', (err) => {
console.log(err.message);
response.status(404).send();
fileStream.removeAllListeners();
});
fileStream.on('end', () => {
console.log('Streamed successfully to user');
fileStream.removeAllListeners();
});
// finally starting the stream
fileStream.pipe(response);
}
Thanks for your help ! I've just found my error !
I forgot to add ReponseType: blob and now it works perfectly ;-)
downloadFile = (filetodownload) => {
axios.get('http://localhost:4000/cww/download/'+filetodownload, {responseType: 'blob'})
.then(res => {
var filename = "testfile.pdf"
download(res.data, scriptname, "text/plain");
});
}

How to generate excel file using node.js?

Hi I am go for the generate excel file form the array but I am not getting successes. I am work using node.js and I am use npm package for generate excel file but I am not getting any data in excel file. excel is generate but not getting any type of data in my file. so any one know where is my mistake then please let me know how can fix it.
This is my array and query =>
var XLSX = require('xlsx');
var Array = [];
Array.push({
username: 'Carakc',
fullName: 'Crack',
followingCount: 2655,
followerCount: 466,
biography: 'I am new man'
},
{
username: 'mahi',
fullName: 'Fit',
followingCount: 3011,
followerCount: 385,
biography: 'hello everyone!'
})
app.get(prefix + '/GetFollowersInExcel', function (req, res, next) {
var ws = XLSX.utils.json_to_sheet(Array);
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Followres");
var wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' });
res.end(wbout, 'binary');
}
});
}
});
})
This is my service code =>
GetFollowersInExcel: function (InstaId) {
return $http({
method: "GET",
url: ONURL + "GetFollowersInExcel",
responseType: "arraybuffer",
headers: {
'Content-type': 'application/json',
'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
}
}).then(function (data, status, xhr) {
debugger;
if (data.data.byteLength > 0) {
var file = new Blob([data.data], { type: 'application/binary' });
var fileURL = URL.createObjectURL(file);
$('#getexcel').show();
var link = document.createElement('a');
link.href = fileURL;
link.download = "myfile.xlsx";
link.click();
URL.revokeObjectURL(file);
}
}, function (error) {
return error;
})
},
using this wave I am getting like this data in excel =>
I want like this data in excel file =>
I've tried your first code and I've found no errors, the resulting xlsx is perfect.
Peheraps I've found the problem: var Array is declared outside the app.get callback... Are you sure that your var Array can be correctly reached by XLSX.utils.json_to_sheet? it's in the same scope? or it's declared somewhere inaccessible?
try to declare it inside the callback and probably all will work well, and, if this is the case, you can use a class or a method to retrieve the var from outside ("how" depends on your code)
P.s. change the name of the var, is not a good habit overwrite the Array object ;)

Resources