PhantomJS PDF file to Base64 - node.js

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?

Related

How to save a text to speech audio file client side?

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");
});

Use Firebase Function with error TypeError

I am very new to Node js, I just want to get the data from extenal xml from a website but I got an error from Firebase Function log TypeError: invalid media type. I think it come from when I try to do this task parseString(xml, function(err, result) { })
Anyone can help me, it will be great:
Here is my code on firebase function:
exports.getRate = functions.https.onRequest((req, res) => {
getRate = () => {
var url = "https://www.vietcombank.com.vn/ExchangeRates/ExrateXML.aspx";
https.get(url, function(res) {
var xml = "";
res.on('error', function(error){
console.log(error, 'get data error');
})
res.on("data", function(chunk) {
xml += chunk;
console.log(xml, 'xml file');
});
res.on("end", function() {
var date = "";
let rateAUD = {
code: 'AUD/VND',
buy: 0,
sell: 0
};
let rateUSD = {
code: 'USD/VND',
buy: 0,
sell: 0
};
parseString(xml, function(err, result) {
console.log(xml, 'xml file');
date = result.ExrateList.DateTime[0];
if (result.ExrateList.Exrate[0].$.CurrencyCode == "AUD") {
rateAUD.buy = result.ExrateList.Exrate[0].$.Buy;
rateAUD.sell = result.ExrateList.Exrate[0].$.Sell;
} else {
console.log("They change the database list");
}
if (result.ExrateList.Exrate[18].$.CurrencyCode == "USD") {
rateUSD.buy = result.ExrateList.Exrate[18].$.Buy;
rateUSD.sell = result.ExrateList.Exrate[18].$.Sell;
} else {
console.log("They change the database list");
}
console.log(rateAUD, rateUSD, 'get data');
uploadDataToServer(date, { rateAUD, rateUSD });
if(err) {
console.log(err);
}
});
});
});
};
function uploadDataToServer(date, { rateAUD, rateUSD }) {
var db = admin.firestore();
let data = { rateAUD, rateUSD };
data.timeStamp = date;
console.log('upload success');
db.collection("liveRate").add(data),then((err)=> {
console.log(err);
});
}
return res.status(200)
.type('application / json')
.send('hello')
});
'
When I run the same code on another Nodejs playground, it works well.
Here is the link: https://repl.it/repls/MaroonSlateblueProfiler
So weird!
Ps: my payment option is ON.
The problem is that the client is sending the server what may or may not be a valid media type in an encoding the server cannot understand (as per the Content-Encoding header the client packaged with the request message).
Please try to set the content-type to xml:
getRate = () => {
var options = {
hostname: "www.vietcombank.com.vn",
port: 443,
path: "/ExchangeRates/ExrateXML.aspx",
headers: {
'Content-Type': 'application/xml'
}
};
https.get(options, function(res) {
...
});
}

NodeJS - prompt download CSV file

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

Why isn't node serving my image file?

I have a vanilla node.js http server. Everything except my image file works. I just get the broken image icon on the page.
Here is my server code:
"use strict";
class app {
constructor() {
app.loadServer();
}
static loadServer() {
const HTTP = require('http'),
PORT = 1337,
SERVER = HTTP.createServer(function(req, res) {
let httpHandler = function(err, str, contentType) {
console.log('\n\n' + 'Content type: ' + contentType + '\n\n');
if (err) {
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end('An error has occurred: ' + err.message);
} else if (contentType.indexOf('image') >= 0) {
res.writeHead(200, { 'Content-Type': contentType });
res.end(str, 'binary');
} else {
res.writeHead(200, { 'Content-Type': contentType });
res.end(str);
}
};
if (req.headers['x-requested-with'] === 'XMLHttpRequest') {
if (req.method == 'POST') {
app.getFormData(req, res);
} else {
console.log("[405] " + req.method + " to " + req.url);
res.writeHead(405, "Method not supported", { 'Content-Type': 'text/html' });
res.end('<html><head><title>405 - Method not supported</title></head><body><h1>Method not supported.</h1></body></html>');
}
} else if (req.url.indexOf('/javascripts/') >= 0) {
app.render(req.url.slice(1), 'application/ecmascript', httpHandler);
} else if (req.url.indexOf('/css/') >= 0) {
app.render(req.url.slice(1), 'text/css', httpHandler);
} else if (req.url.indexOf('/images/') >= 0) {
app.render(req.url.slice(1), 'image/jpg', httpHandler);
} else {
app.render('public/views/index.html', 'text/html', httpHandler);
}
}).listen(PORT, function() {
console.log('-= Francis Server Listening at http://127.0.0.1:' + PORT + ' =-');
});
}
static render(path, contentType, callback) {
const FS = require('fs');
FS.readFile(__dirname + '/' + path, 'utf-8', function(err, str) {
callback(err, str, contentType);
});
}
static getFormData(req, res) {
const FORMIDABLE = require('formidable'),
DO_NAMES = require('./node/NameClass');
let formData = {};
new FORMIDABLE.IncomingForm().parse(req)
.on('field', function(field, name) {
formData[field] = name;
})
.on('error', function(err) {
next(err);
})
.on('end', function() {
let finalName = new DO_NAMES(formData);
res.writeHead(200, {'content-type': 'text/plain'});
res.write('-= Received form: ');
res.end(finalName.getFirstName() + ' ' + finalName.getLastName());
});
}
}
module.exports = app;
It feels like it's trying to serve the image as text instead of picture. I verified that the image is there and readable.
I found the problem.
it happens here:
FS.readFile(__dirname + '/' + path, 'utf-8', function(err, str) {
callback(err, str, contentType);
});
You read the image file as UTF-8 but it is a binary file. That is why the image data is corrupt. Instead you have to use binary as encoding.
You could change your code like this:
static render(path, contentType, callback, encoding) {
const FS = require('fs');
FS.readFile(__dirname + '/' + path, encoding ? encoding : 'utf-8', function(err, str) {
callback(err, str, contentType);
});
}
and then call render like this:
app.render(req.url.slice(1), 'image/jpeg', httpHandler, 'binary');
There are obviously better ways to do it but this requires a minimum amount of change to your code. Just make sure the readFile() encoding is binary for binary files.
Also the correct mime type for jpg is image/jpeg not image/jpg. Most, if not all, browsers won't care but it is more clean.
It looks like your NODE server is setting the wrong MIME type. You can set the MIME type yourself, as you are doing, but this gets awfully painful. I would recommend using a MIME type node module that is made for this exact purpose.
https://www.npmjs.com/package/mime
This npm package does exactly this with very little effort.

NodeJS - Stream video that is being downloaded

I am writing a program that will stream a video file that is currently being downloaded onto the drive. The problem I am having seems to be getting the browser to actually play the video. the script listens for file changes, and then streams the rest, but the browser doesnt do anything besides display a blank Video page.
var fs = require('fs'),
http = require('http'),
filename = '/home/qrpike/Videos/topgears.mp4';
http.createServer(function (req, res) {
console.log(req.url);
if( req.url == '/video.mp4'){
res.writeHead(200,{
'Content-Type' : 'video/mp4',
'Cache-Control' : 'public',
'Connection' : 'keep-alive',
'Content-Disposition' : 'inline; filename=topgears.mp4;',
'Content-Transfer-Encoding' : 'binary',
'Transfer-Encoding' : 'chunked'
});
fs.open(filename, 'r', function(err, fd) {
if (err) throw new Error('Could not open file');
var position = 0;
fs.stat(filename, read);
fs.watchFile(filename, read.bind(null, null));
function read(err, stat) {
var delta = stat.size - position;
if (delta <= 0) return;
fs.read(fd, new Buffer(delta), 0, delta, position, function(err, bytes, buffer) {
console.log("err", err, "bytes", bytes, "position",position,"delta",delta);
res.write(buffer.toString('binary'));
});
position = stat.size;
}
});
}
}).listen(1337);
console.log('Server running at http://127.0.0.1:1337/');
So this answer depends on growing-file, which in theory does what you want. My concern is that the project hasn't had a commit in two years so I have no idea if it still works. That being said, this worked for me locally (though I didn't test piping to the video file):
var fs = require('fs');
var http = require('http');
var GrowingFile = require('growing-file');
var FILENAME = '/home/dave/Desktop/video/video.ogg';
var server = http.createServer(function(req, res) {
var file;
if (req.url === '/video.ogg') {
res.writeHead(200, {
'Content-Type': 'video/ogg'
});
file = GrowingFile.open(FILENAME);
file.pipe(res);
} else {
res.statusCode = 404;
res.end('Not Found');
}
});
server.listen(1337);

Resources