Finally, after weeks of self study I've managed to get PhantomJS (running in NodeJS) to process a file, with attributes, output as a PNG or PDF and this is working perfectly.
However, I've been stuck the last few days and hoping someone can give me a pointer.
When PhantomJS has created the PDF, AND my childprocess has completed, how can I show a dialog box asking the user if they wish to download the file?
I've used the following code:
childProcess.execFile(binPath, childArgs,
function(err, stdout, stderr) {
console.log('Child Processing complete!');
download('myservername','test.png',function(){
console.log('done')
});
}
);
With the "download" function as the following:
var download = function(uri, filename, callback){
request.head(uri, function(err, res, body){
console.log('content-type:', res.headers['content-type']);
console.log('content-length:', res.headers['content-length']);
var r = request(uri).pipe(fs.createWriteStream(filename));
r.on('close', callback);
});
};
But I just can't seem to get a prompt on the screen asking me to download "test.png"
Is there something obvious I'm missing?
* EDIT *
Actual code here:
This is the page that calls:
var util = require("util"),
path = require('path'),
fs = require('fs'),
request = require('request'),
url = require('url'),
http = require('http');
var phantomjs = require('phantomjs'),
path = require('path'),
childProcess = require('child_process');
childProcess2 = require('child_process');
var express = require('express');
var app = express();
var download = function(uri, filename, callback){
request.head(uri, function(err, res, body){
console.log('content-type:', res.headers['content-type']);
console.log('content-length:', res.headers['content-length']);
var r = request(uri).pipe(fs.createWriteStream(filename));
r.on('close', callback);
});
};
var binPath = phantomjs.path;
http.createServer(function(request,response){
console.log("Webpage was called");
var childArgs = [path.join(__dirname, 'tst2.js'),''];
childProcess.execFile(binPath, childArgs,
function(err, stdout, stderr) {
console.log('Child Processing complete!');
download('[here is the url of my server with filename.png','test2.png',function(){
console.log('done')
});
});
response.writeHeader(200, {"Content-Type": "text/plain"});
response.write("Processing Server");
response.end();
}).listen(8080);
and the phantomJS page is here:
"use strict";
var page = require('webpage').create();
page.viewportSize = { width: 1663, height : 768};
page.paperSize = {
width: '11in',
height: '8.5in'
}
page.orientation='landscape'
page.onResourceRequested = function(requestData, request) {
console.log('::loading', requestData['url']);
};
page.onLoadFinished = function() {
console.log('::rendering');
page.render('test.png');
phantom.exit();
};
var content='';
content += '<html><head>'
content +='</head><body>';
content+='<div class="col-md-12 col-lg-12 results">'
content+='<div class="col-md-8 detailsPanel">'
content+='</div>'
content +='</body></html>';
page.content=content;
Like I said, they both work perfectly, the webpage I call is running on port 8080, I can see the console.logs appearing in a command box on Windows Server, the PNG file is rendered perfectly, but after that it just hangs there and I get no download prompt.
Related
I'm make a webscraper and I already know how to scrap some data and convert them to Json with this code I made :
var express = require('express');
var fs = require('fs');
var request = require('request');
var cheerio = require('cheerio');
var app = express();
var url = 'http://www.footmercato.net/';
request(url, function(err, resp, body) {
if (!err) {
var $ = cheerio.load(body);
var data = [];
var i = 1;
$('.text').each(function(i, element) {
var article = $('p');
var jsObject = { title : "", article : "", date : "" };
var articleTxt = article.text();
jsObject.article = articleTxt;
data.push(jsObject);
})
var json = JSON.stringify(data);
fs.writeFile('output.json', JSON.stringify(json, null, 4), function(err) {
console.log('File successfully written!');
})
}
});
app.listen('8080');
But I would like to navigate to the website I'm scraping, fill out form and going to others pages.
Does somebody know if i can do it with cheerio or how I can add it to my existing code ?
Thanks
You can use webdriverio actually he will open a browser window, and then you can manipulate the dom through the webdriverio api to handle forms mouse clicks, and navigate from one page to an other.
var webdriverio = require('webdriverio');
var options = {
desiredCapabilities: {
browserName: 'firefox'
}
};
webdriverio
.remote(options)
.init()
.url('http://www.google.com')
.getTitle().then(function(title) {
console.log('Title was: ' + title);
})
.end();
I'm trying to download a Youtube video audio using the ytdl-core module (https://github.com/fent/node-ytdl-core).
I wrote an API using Express which lets me download an audio by its URL:
app.get('/api/downloadYoutubeVideo', function (req, res) {
res.set('Content-Type', 'audio/mpeg');
var videoUrl = req.query.videoUrl;
var videoName;
ytdl.getInfo(videoUrl, function(err, info){
videoName = info.title.replace('|','').toString('ascii');
res.set('Content-Disposition', 'attachment; filename=' + videoName + '.mp3');
});
var videoWritableStream = fs.createWriteStream('C:\\test' + '\\' + videoName); // some path on my computer (exists!)
var videoReadableStream = ytdl(videoUrl, { filter: 'audioonly'});
var stream = videoReadableStream.pipe(videoWritableStream);
});
The problem is that when I call this API I get a 504 error from my server.
I want to be able to save this downloaded audio on my local disk.
Help would be appreciated. Thank you
Well for some reason the videoName was undefined so it messed up my function...
Here is the correct code after a few changes and adding the destination path as query variable.
app.get('/api/downloadYoutubeVideo', function (req, res) {
var videoUrl = req.query.videoUrl;
var destDir = req.query.destDir;
var videoReadableStream = ytdl(videoUrl, { filter: 'audioonly'});
ytdl.getInfo(videoUrl, function(err, info){
var videoName = info.title.replace('|','').toString('ascii');
var videoWritableStream = fs.createWriteStream(destDir + '\\' + videoName + '.mp3');
var stream = videoReadableStream.pipe(videoWritableStream);
stream.on('finish', function() {
res.writeHead(204);
res.end();
});
});
});
This is basic imagemagick setup for node.js that i came across lately. I understand that question i'm asking is very newbie but i'm new to node js and imagemagick and i wanted to try to make it dynamic. Current script bellow is cropping one specific file, is there a way to make it to apply conversion to all the files from directory /before and then output it to directory /after ? Also the operation i want to do for all the images in directory is to apply watermark on each (crop operation in code is just from example).
var http = require("http");
var im = require("imagemagick");
var args = [
"image.png",
"-crop",
"120x80+30+15",
"output.png"
];
var server = http.createServer(function(req, res) {
im.convert(args, function(err) {
if(err) { throw err; }
res.end("Image crop complete");
});
}).listen(8080);
Yes, you can do it by implementing foreach by all files itself.
Also you will need to install async module:
npm install async
Example code:
var async = require('async'),
fs = require('fs'),
im = require('imagemagick'),
maxworkers = require('os').cpus().length,
path = require('path');
module.exports = resize;
function resize(params) {
var queue = async.queue(resizeimg, maxworkers);
fs.readdir(params.src, function(err, files) {
files.forEach(function(file) {
queue.push({
src: path.join(params.src, '/', file),
dest: path.join(params.dest, '/', file),
width: params.width,
height: params.height
})
});
});
}
function resizeimg(params, cb) {
var imoptions = {
srcPath: params.src,
dstPath: params.dest
};
if (params.width !== undefined) imoptions.width = params.width;
if (params.height !== undefined) imoptions.height = params.height
im.resize(imoptions, cb);
}
Then in you code:
var http = require("http");
var resize = require("./resize");
var server = http.createServer(function(req, res) {
resize({
src: '/source/folder',
dest: '/destination/folder',
width: 300
});
}).listen(8080);
i need to send a PDF file from angularjs client to NodeJS service.
I did the angularjs service, and when i receive the file its a string like this:
%PDF-1.3
3 0 obj
<</Type /Page
/Parent 1 0 R
/Reso
How can i reconvert this string to PDF in NodeJS?
This is the client code:
var sendByEmail = function () {
$scope.generatingPdf = true;
$('#budget').show();
var pdf = new JsPDF('p', 'pt', 'letter');
var source = $('#budget')[0];
pdf.addHTML(source, 0, 0, function () {
var resultPdf = pdf.output();
BillService.sendByEmail("rbrlnx#gmail.com", resultPdf).then(function () {
});
$('#budget').hide();
});
};
var sendByEmail = function (email, file) {
var deferred = $q.defer();
var data = {
email: email,
file: file
};
BillService.sendByEmail(data, function (result) {
deferred.resolve(result);
}, function () {
deferred.reject();
});
return deferred.promise;
};
The server code controller its empty:
var sendByEmail = function (req, res, next) {
var file = req.body.file;
};
I experimented with this a while ago, and I came up with this. It's not production ready by a long shot maybe you find it useful. It's free of front end libraries (except Angular ofcourse), but assumes you're using Express 4x and body-parser.
The result:
In the browser:
On the server:
What you're seeing:
You're seeing a tiny node server, serving static index.html and angular files, and a POST route receiving a PDF in base64 as delivered by the HTML FileReader API, and saves it to disk.
Instead of saving to disk, you can send it as an email attachment. See for instance here or here for some info on that.
The example below assumes uploading a PDF by a user through a file input, but the idea is the same for all other ways of sending a document to your back end system. The most important thing is to send the pdf data as BASE64, because this is the format that most file writers and email packages use (as opposed to straight up binary for instance..). This also goes for images, documents etc.
How did I do that:
In your HTML:
<div pdfs>Your browser doesn't support File API.</div>
A directive called pdfs:
myApp.directive('pdfs', ['upload', function(upload) {
return {
replace: true,
scope: function() {
files = null;
},
template: '<input id="files" type="file">',
link: function(scope,element) {
element.bind('change', function(evt) {
scope.$apply(function() {
scope.files = evt.target.files;
});
});
},
controller: function($scope, $attrs) {
$scope.$watch('files', function(files) {
//upload.put(files)
if(typeof files !== 'undefined' && files.length > 0) {
for(var i = 0; i<files.length;i++) {
readFile(files[i])
}
}
}, true);
function readFile(file) {
var reader = new FileReader();
reader.addEventListener("loadend", function(evt) {
upload.post({name: file.name, data: reader.result})
})
if(reader.type = 'application/pdf') {
reader.readAsDataURL(file);
}
}
}
}
}]);
A tiny service:
myApp.service('upload', function($http) {
this.post = function(file) {
$http.post('/pdf', file);
}
});
And a node server:
var express = require('express');
var bodyParser = require('body-parser')
var fs = require("fs");
var app = express();
app.use(express.static('.'));
app.use( bodyParser.json({limit: '1mb'}) );
app.post('/pdf', function(req, res){
var name = req.body.name;
var pdf = req.body.data;
var pdf = pdf.replace('data:application/pdf;base64,', '');
res.send('received');
fs.writeFile(name, pdf, 'base64', function(err) {
console.log(err);
});
});
var server = app.listen(3000, function() {
console.log('Listening on port %d', server.address().port);
});
I'm trying to upload a image from a AngularJS interface to a nodejs server (expressjs).
(I'm using mean.io)
Every time I upload someting, req.body logs "{}" and req.files logs "undefined"
I'm using angular-file-upload directive in AngularJS
Client-side code:
$scope.onFileSelect = function() {
console.log($files);
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
$scope.upload = $upload.upload({
url: 'map/set',
method: 'POST',
headers: {'enctype': 'multipart/form-data'},
data: {myObj: $scope.myModelObj},
file: file,
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
});
}
};
Server-side code
var app = express();
require(appPath + '/server/config/express')(app, passport, db);
app.use(bodyParser({uploadDir:'./uploads'}));
app.post('/map/set', function(req, res) {
console.log(req.body);
console.log(req.files);
res.end('Success');
});
*****Edit*****
HTML Code
<div class="row">
<input id="file" type="file" ng-file-select="onFileSelect()" >
</div>
Hand built request
$scope.onFileSelect = function() {
//$files: an array of files selected, each file has name, size, and type.
//console.log($files);
var xhr = new XMLHttpRequest();
// not yet supported in most browsers, some examples use
// this but it's not safe.
// var fd = document.getElementById('upload').getFormData();
var fd = new FormData();
var files = document.getElementById('myfileinput').files;
console.log(files);
for(var i = 0;i<files.length; i++) {
fd.append("file", files[i]);
}
/* event listeners */
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("abort", uploadCanceled, false);
function uploadComplete(){
console.log("complete");
}
function uploadProgress(){
console.log("progress");
}
function uploadFailed(){
console.log("failed");
}
function uploadCanceled(){
console.log("canceled");
}
xhr.open("POST", "map/set");
xhr.send(fd);
};
The latest version of mean.io uncluding express 4.x as dependency. In the documentation for migration express 3 to 4 you can read, express will no longer user the connect middlewares. Read more about here: https://github.com/visionmedia/express/wiki/Migrating-from-3.x-to-4.x
The new body-parser module only handles urlencoded and json bodies. That means for multipart bodies (file uploads) you need an additional module like busboy or formadible.
Here is an example how I use angular-file-upload with busboy:
The AngularJS Stuff:
$upload.upload({
url: '/api/office/imageUpload',
data: {},
file: $scope.$files
}) …
I write a little helper module to handle uploads with busboy easier. It’s not very clean coded, but do the work:
var env = process.env.NODE_ENV || 'development';
var Busboy = require('busboy'),
os = require('os'),
path = require('path'),
config = require('../config/config')[env],
fs = require('fs');
// TODO: implement file size limit
exports.processFileUpload = function(req, allowedExtensions, callback){
var busboy = new Busboy({ headers: req.headers });
var tempFile = '';
var fileExtenstion = '';
var formPayload = {};
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
fileExtenstion = path.extname(filename).toLowerCase();
tempFile = path.join(os.tmpDir(), path.basename(fieldname)+fileExtenstion);
file.pipe(fs.createWriteStream(tempFile));
});
busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) {
var jsonValue = '';
try {
jsonValue = JSON.parse(val);
} catch (e) {
jsonValue = val;
}
formPayload[fieldname] = jsonValue;
});
busboy.on('finish', function() {
if(allowedExtensions.length > 0){
if(allowedExtensions.indexOf(fileExtenstion) == -1) {
callback({message: 'extension_not_allowed'}, tempFile, formPayload);
} else {
callback(null, tempFile, formPayload)
}
} else {
callback(null, tempFile, formPayload)
}
});
return req.pipe(busboy);
}
In my controller i can use the module that way:
var uploader = require('../helper/uploader'),
path = require('path');
exports.uploadEmployeeImage = function(req,res){
uploader.processFileUpload(req, ['.jpg', '.jpeg', '.png'], function(uploadError, tempPath, formPayload){
var fileExtenstion = path.extname(tempPath).toLowerCase();
var targetPath = "/exampleUploadDir/testFile" + fileExtenstion;
fs.rename(tempPath, targetPath, function(error) {
if(error){
return callback("cant upload employee image");
}
callback(null, newFileName);
});
});
}
I'm going to take a guess here that the header settings are incorrect.
headers: {'enctype': 'multipart/form-data'},
Should be changed to:
headers: {'Content-Type': 'multipart/form-data'},
Ensure you have an 'id' AND 'name' attribute on the file input - not having an id attribute can cause problems on some browsers. Also, try building the request like this:
var xhr = new XMLHttpRequest();
// not yet supported in most browsers, some examples use
// this but it's not safe.
// var fd = document.getElementById('upload').getFormData();
var fd = new FormData();
var files = document.getElementById('myfileinput').files;
for(var i = 0;i<files.length; i++) {
fd.append("file", files[i]);
}
/* event listeners */
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("abort", uploadCanceled, false);
xhr.open("POST", "your/url");
xhr.send(fd);
angular isn't great with file uploads so doing it by hand might help.