I'm trying to get - after some promises have been executed - a CSV result back together with a status reponse having details.
The response does get me the data for the CSV but I cannot seem to get the browser to download this data into a CSV file.
router.post('/getSalesOrders', function (request, response) {
var data = request.body;
salesOrderActions.retrieveSalesOrders(data) //
.then(function (result) {
response.setHeader('Content-disposition', 'attachment; filename=testing.csv');
response.set('Content-Type', 'text/csv');
response.json(result[0].message).send(result[0].file);
})
.catch(function (err) {
console.log(err);
if (err.statusCode) {
response.json(err);
}
else {
var error = output.getCriticalErrorResult(c.titles.SERVICE_CRITICAL_ERROR, c.messages.UNKNOWN_ERROR, err.message);
response.json(error);
}
});
});
My result object gets created in the salesOrderActions:
I am here using npm package json2csv
var fields = ['id',.....];
var csv = csvParser({ data: unmatchedLines, fields: fields });
return {
file: csv,
message:
output.getSuccessResult(
titles.SALES_ORDER_SUCCESS_RETRIEVAL,
salesDataForModel.identifier
)
}
My response to the browser is as follows:
So my message isn't sent it seems and I do get the CSV data but not as a file to download.
How can I manage that?
As a sidenote maybe, my front-end is React
EDIT
Response with octed headers:
Try:
sending Content-Type before Content-Disposition
quoting the filename: filename="testing.csv"
Also HTTP headers are case insensitive, so it should not make a difference, but you should write Content-Disposition (capital D).
response.set('Content-Type', 'text/csv');
response.setHeader('Content-Disposition', 'attachment; filename="testing.csv"');
If this does not work you can change the Content-Type to application/octet-stream
This always forces the browser to download the data sent from the server.
Try this code:
router.post('/getSalesOrders', function (request, response) {
var data = request.body;
var fs = require('fs');
salesOrderActions.retrieveSalesOrders(data) //
.then(function (result) {
//**********
var file = "testing.csv";
response.setHeader('Content-disposition', 'attachment; filename=testing.csv');
response.set('Content-Type', 'text/csv');
var filestream = fs.createReadStream(file);
filestream.pipe(res);
//*********
})
.catch(function (err) {
console.log(err);
if (err.statusCode) {
response.json(err);
}
else {
var error = output.getCriticalErrorResult(c.titles.SERVICE_CRITICAL_ERROR, c.messages.UNKNOWN_ERROR, err.message);
response.json(error);
}
});
});
So actually it turns out it is because I'm doing an Ajax request which doesn't - by default - prompt the browser to download any files.
What I did in the end:
router.post('/getSalesOrders', function (request, response) {
var data = request.body;
salesOrderActions.retrieveSalesOrders(data)
.then(function (result) {
response.json(result);
})
.catch(function (err) {
//...
});
});
And then in my front-end, when receiving the result:
salesOrderService.retrieveSalesOrderData()
.then(function (result) {
self.convertAndDownloadCsv(result.unmatchedLines);
});
convertAndDownloadCsv: function (data) {
if (data && data.length > 0) {
var csvData = csvProcessor({ //using csv2json node js package
data: data,
quotes: '',
del: ';'
});
var filename = "unmatchedLinesFromSalesOrders.csv";
var blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' });
if (navigator.msSaveBlob) { // IE 10+
navigator.msSaveBlob(blob, filename);
} else {
var link = document.createElement("a");
if (link.download !== undefined) { // feature detection
// Browsers that support HTML5 download attribute
var url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", filename);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
}
}
More info can be found here
Related
I have AWS Lambda function that return pdf file like arr.
I want to call function and save pdf file, but after saving I can't open it, it brocken. I cant undestand why, i tried differente ways to create pdf, by the way i can get arr and using online converter convert arr to file, and its work, but when i create pdf file using node code its always broken. I tried internal node moduls like fs, and external like pdfkit.
`const https = require('https');
const fs = require('fs');
const PDFDocument = require('pdfkit');
const options = {
host: 'uek9w0hztc.execute-api.eu-north-1.amazonaws.com',
path: '/pdfmaker',
method: 'POST',
headers: {
url: 'https://www.linkedin.com'
}
}
const req = https.request(options, res => {
let data = [];
const headerDate = res.headers && res.headers.date ? res.headers.date : 'no response date';
console.log('Status Code:', res.statusCode);
console.log('Date in Response header:', headerDate);
res.on('data', chunk => {
data.push(chunk);
});
res.on('end', () => {
console.log('Response ended: ');
// fs.writeFileSync('index.pdf', Buffer.from(data));
// fs.writeFileSync("index_v2.pdf", Buffer.from(data), 'binary', (err) => {
// if(err) {
// return console.log(err);
// }
// console.log("The file was saved!");
// });
// const doc = new PDFDocument();
// doc.pipe(fs.createWriteStream('output.pdf'));
let writeStream = fs.createWriteStream('pdf123.pdf')
writeStream.once('open', (fd) =>{
writeStream.write(new Buffer.from(data, 'binary'))
writeStream.on('finish', () => {
console.log('wrote all data to file');
});
writeStream.end()
})
});
}).on('error', err => {
console.log('Error: ', err.message);
});
req.end();`
I tried internal node moduls like fs, and external like pdfkit.
I expect someone give me a hint where the problem is.
desired behaviour
allow user to download text to speech audio file by clicking a button, like this official demo:
https://text-to-speech-starter-kit.ng.bluemix.net
what i've tried
i am using:
https://github.com/watson-developer-cloud/node-sdk
i can generate an audio file server side but can't figure out how to send that file back to the client for them to save - so i am trying to generate it client side instead.
attempt 01: generate audio file server side
server.js (works)
const fs = require('fs');
const TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
const textToSpeech = new TextToSpeechV1({
iam_apikey: '{apikey}',
});
const synthesizeParams = {
text: 'Hello world',
accept: 'audio/wav',
voice: 'en-US_AllisonVoice',
};
textToSpeech.synthesize(synthesizeParams)
.then(audio => {
audio.pipe(fs.createWriteStream('hello_world.wav'));
})
.catch(err => {
console.log('error:', err);
});
for reference, according to the docs, the .synthesize() method response type is:
NodeJS.ReadableStream|FileObject|Buffer
attempt 02: generate audio file client side
server.js - required to get token (works)
var AuthorizationV1 = require('ibm-watson/authorization/v1');
var iam_apikey = local_settings.TEXT_TO_SPEECH_IAM_APIKEY;
var url = local_settings.TEXT_TO_SPEECH_URL;
var authorization = new AuthorizationV1({
iam_apikey: iam_apikey,
url: url
});
const api_tts_token_get = async (req, res) => {
authorization.getToken(function(err, token) {
if (!token) {
console.log('error:', err);
} else {
res.json({ token: token, url: url });
}
});
}
app.route("/api/:api_version/text-to-speech/token")
.get(api_tts_token_get);
client.js (doesn’t work)
var TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
const get_token = (parameters) => {
$.ajax({
url: "/api/v1/text-to-speech/token",
data: parameters,
dataType: 'json',
cache: false,
headers: headers,
success: function(results) {
var token = results.token;
var url = results.url;
var textToSpeech = new TextToSpeechV1({ token: token, url: url });
var synthesizeParams = {
text: 'hello world!',
accept: 'audio/wav',
voice: 'en-US_AllisonV3Voice'
};
textToSpeech.synthesize(synthesizeParams, function(err, result) {
if (err) {
return console.log(err);
}
console.log(result);
});
},
statusCode: {
500: function() {
console.log("that didn't work");
}
}
});
}
webpack.config.js
added per instructions at:
https://github.com/watson-developer-cloud/node-sdk/tree/master/examples/webpack#important-notes
node: {
// see http://webpack.github.io/docs/configuration.html#node
// and https://webpack.js.org/configuration/node/
fs: 'empty',
net: 'empty',
tls: 'empty'
},
chrome dev tools errors:
xhr.js:108 Refused to set unsafe header "User-Agent"
The provided value 'stream' is not a valid enum value of type XMLHttpRequestResponseType.
Access to XMLHttpRequest at 'https://***.watsonplatform.net/text-to-speech/api/v1/synthesize?voice=en-US_AllisonV3Voice'
from origin 'http://localhost:3000' has been blocked by CORS policy:
Request header field x-ibmcloud-sdk-analytics is not allowed by
Access-Control-Allow-Headers in preflight response.
Error: Response not received. Body of error is HTTP ClientRequest object
at RequestWrapper.formatError (requestwrapper.js:218)
at eval (requestwrapper.js:206)
Here is one solution i have figured out.
It generates the audio file server side and sends it back via res.download().
The only caveat is that you can't use $.ajax() but rather something like:
window.open("/api/v1/audio?file_id=12345");
server.js
var TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
const api_audio_get = async (req, res) => {
var query_parameters = req.query;
var file_id = query_parameters.file_id;
var textToSpeech = new TextToSpeechV1({
iam_apikey: local_settings.TEXT_TO_SPEECH_IAM_APIKEY,
url: local_settings.TEXT_TO_SPEECH_URL
});
const synthesizeParams = {
text: 'here is test voice',
accept: 'audio/wav',
voice: 'en-US_AllisonV3Voice',
};
textToSpeech.synthesize(
synthesizeParams,
function(err, audio) {
if (err) {
console.log(err);
return;
}
// see: https://stackoverflow.com/a/46413467
// this allows you to create temp file on server, send it, then delete it
var filename = file_id + ".wav";
var absPath = path.join(__dirname, "/my_files/", filename);
var relPath = path.join("./my_files", filename); // path relative to server root
// see: https://nodejs.org/en/knowledge/advanced/streams/how-to-use-fs-create-write-stream/
var write_stream = fs.createWriteStream(relPath);
// audio is written to the writestream
audio.pipe(write_stream);
// see: https://stackoverflow.com/questions/19829379/detecting-the-end-of-a-writestream-in-node
write_stream.on('finish', function() {
res.download(absPath, (err) => {
if (err) {
console.log(err);
}
fs.unlink(relPath, (err) => {
if (err) {
console.log(err);
}
console.log("FILE [" + filename + "] REMOVED!");
});
});
});
}
);
}
// route handler
app.route("/api/:api_version/audio")
.get(api_audio_get);
client.js
$(document).on("click", ".download_audio", function() {
window.open("/api/v1/audio?file_id=12345");
});
Hi guys i'm trying to download a pdf file and save it on my disk. The API send me a string. But the following code not working.
axios.get('https://myapi.com/download', config).then((res) => {
var buff = Buffer.from(res.data, 'binary');
fs.writeFile('file.pdf', buff, function (err) {
if (err) throw err;
console.log('Saved!');
});
}).catch((e) => {
console.log(e);
})
I've tried it, and working ...
fs.readFile('./download.pdf','binary', function (err, data) {
var str = data.toString();
var buff = Buffer.from(str, 'binary');
fs.writeFile('novopdf.pdf',buff, () => {
console.log('ok');
})
});
You need to config axios get request as follows
const response = await Axios({
method: 'GET',
url: url,
responseType: 'stream'
})
response.data.pipe(Fs.createWriteStream(path)) // path is location where you want to write the file.
Then check for end event on the response object.
I write API in order to client upload file. API has content-type multiple/form-data. But I don't know get values from client send to my
router.post('/upload/file', async (req, res) => {
var body = "";
try {
req.on('data', function (chunk) {
body += chunk;
});
req.on('end', function () {
console.log('body: ' + body);
var formData = new FormData(body);
console.log("=====================", formData.entries);
// var {accessTok, type, file} = req.params;
//
// if (!accessTok || !type || !file) {
res.json({
code: -1000,
message: 'Missing parameter(s). Please provide accessToken, type upload, file upload.'
});
res.end();
return null;
})
// }
}catch(err){
res.json({err: err.message});
res.end();
return;
}
I tried use FormData but not done. I get error is not function, formData.getKey('') is them same.
I'm working with PhantomJS and doing PDF Files from rendering my own website, right now works perfectly (the file is creating, everything seems just ok at localhost).
All this comes from a Post all the way from my actual server (NoseJS), i'm trying to send in response the .pdf PhantomJS made, I tried sending binary, base64 and all kind of ways I found in the web... Right now i'm converting the file to base64 in phantom and sending it to Node, Node creates the file and the file comes in blank and bigger in size (Phantom file: 15Mb, Node file: 29Mb)
My question is, does anyone know an easy way through?
My code right now:
PhantomJS:
function utf8_to_b64(str) {
return window.btoa(unescape(encodeURIComponent(str)));
}
var server = require('webserver').create();
var fs = require('fs');
var port = require('system').env.PORT || 8080; // default back to 8080
server.listen(port, function(request, response) {
if (request.method === 'POST') {
var page = new WebPage();
var objeto = request.post;
page.paperSize = {
format: 'A4',
orientation: 'portrait',
margin: '1cm'
};
page.open("http://google.com", function(status) {
if (status !== 'success') {
console.log('Unable to load the address!');
} else {
//page.render(app + '.pdf', {
page.render('prueba.pdf', {
format: 'pdf',
quality: '100'
});
var archivo = fs.open('prueba.pdf', 'r');
archivo = archivo.read();
archivo = utf8_to_b64(archivo);
console.log('done');
//fs.remove('prueba.pdf');
response.statusCode = 200;
response.headers = {
'Content-type': 'text/plain'
};
response.write(archivo);
response.close();
page.close();
//phantom.exit();
}
});
}
});
NodeJS:
request
.post('http://localhost:8080/')
.send({
app: req.body.app,
url: 'google.com'
})
.end(function(err, respond) {
console.log('post a phantom');
console.log('error: ', err);
fs.writeFile('prueba.pdf', respond.text, 'base64', function(err) {
if (err) throw err;
console.log('It\'s saved!');
});
res.json(resJSON.ok());
});
Any ideas?