How to handle conditional file downloads in meteor.js - node.js

In php you can use headers to force downloads of files, and also to hide actual file locations etc.
This is useful if you only want certain users under certain conditions to be able to download certain files.
How would I do this in meteor? I've played around with the Node.js fs module, and managed to retrieve a binary version of the file on the client. But how would I turn this to an actual file that's downloaded?
Thanks!

Three easy steps:
use meteorite
add the iron-router package: mrt add iron-router
create a server method to serve your file. Here is how an exemple:
Router.map(function () {
this.route('get-image', {
where: 'server',
path: '/img',
action: function () {
console.log('retrieving ' + this.request.query.name);
this.response.writeHead(200, {'Content-type': 'image/png'}, this.request.query.name);
this.response.end(fs.readFileSync(uploadPath + this.request.query.name));
}
});
});
In this example the request is a HTTP GET with one parameter name=name-of-pdf.pdf.
That's really all. Hope it was what you were looking for.

Related

how to prompt where to download zip file created with archiver in node

I am trying to create a zip file in node using the code provided from how to create a zip file in node given multiple downloadable links, as shown below:
var fs = require('fs');
var archiver = require('archiver');
var output = fs.createWriteStream('./example.zip');
var archive = archiver('zip', {
gzip: true,
zlib: { level: 9 } // Sets the compression level.
});
archive.on('error', function(err) {
throw err;
});
// pipe archive data to the output file
archive.pipe(output);
// append files
archive.file('/path/to/file0.txt', {name: 'file0-or-change-this-whatever.txt'});
archive.file('/path/to/README.md', {name: 'foobar.md'});
//
archive.finalize();
When I use this suggestion, the zip file is downloaded without any kind of prompt asking me where I would like to save the file - is there any way I can make it so that a prompt is created asking me where I would like to save the file, which is quite normal these days?
If this is absolutely not possible, would it be possible to always save the file in the downloads folder (regardless of whether on mac or windows or any other operating system)?
So there's a couple of things here. In terms of a 'prompt' or 'pop-up' you won't find anything along the lines of WinForms out of the box, there are options for the command line such as prompts You can use that as your user input.
https://www.npmjs.com/package/prompts
You'll want to use path and more specifically path.join() to combat the mac/windows/linux issue.
Do you need to use path.join in node.js?
You can run an express server and create a route that uses res.download() in which you would provide the zipped file.
https://expressjs.com/en/api.html#res.download

How can I stream multiple remote images to a zip file and stream that to browser with ExpressJS?

I've got a small web app built in ExpressJs that allows people in our company to browse product information. A recent feature request requires that users be able to download batches of images (potentially hundreds at a time). These are stored on another server.
Ideally I think I need to to stream the batch of files to a zip file and stream that to the end user's browser as a download. All preferably without having to store the files on the server. The idea being that I want to reduce load on the server as much as possible.
Is it possible to do this or do I need to look at another approach? I've been experimenting with the 'request' module for the initial download.
If anyone can point me in the right direction or recommend any NPM modules that might help it would be very much appreciated.
Thanks.
One useful module for this is archiver, but I'm sure there are others as well.
Here's an example program that shows:
how to retrieve a list of URL's (I'm using async to handle the requests, and also to limit the # of concurrent HTTP requests to 3);
how to add the responses for those URL's to a ZIP file;
to stream the final ZIP file somewhere (in this case to stdout, but in case of Express you can pipe to the response object).
Example:
var async = require('async');
var request = require('request');
var archiver = require('archiver');
function zipURLs(urls, outStream) {
var zipArchive = archiver.create('zip');
async.eachLimit(urls, 3, function(url, done) {
var stream = request.get(url);
stream.on('error', function(err) {
return done(err);
}).on('end', function() {
return done();
});
// Use the last part of the URL as a filename within the ZIP archive.
zipArchive.append(stream, { name : url.replace(/^.*\//, '') });
}, function(err) {
if (err) throw err;
zipArchive.finalize().pipe(outStream);
});
}
zipURLs([
'http://example.com/image1.jpg',
'http://example.com/image2.jpg',
...
], process.stdout);
Do note that although this doesn't require the image files to be locally stored, it does build the ZIP file entirely in memory. Perhaps there are other ZIP modules that would allow you to work around that, although (AFAIK) the ZIP file format isn't really great in terms of streaming, as it depends on metadata being appended to the end of the file.

NodeJs web crawler file extension handling

I'm developing a web crawler in nodejs. I've created a unique list of the urls in the website crawle body. But some of them have extensions like jpg,mp3, mpeg ... I want to avoid crawling those who have extensions. Is there any simple way to do that?
Two options stick out.
1) Use path to check every URL
As stated in comments, you can use path.extname to check for a file extension. Thus, this:
var test = "http://example.com/images/banner.jpg"
path.extname(test); // '.jpg'
This would work, but this feels like you'll wind up having to create a list of file types you can crawl or you must avoid. That's work.
Side note -- be careful using path. Typically, url is your best tool for parsing links because path is aimed at files/directories, not urls. On some systems (Windows), using path to manipulate a url can result in drama because of the slashes involved. Fair warning!
2) Get the HEAD for each link & see if content-type is set to text/html
You may have reasons to avoid making more network calls. If so, this isn't an option. But if it is OK to make additional calls, you could grab the HEAD for each link and check the MIME type stored in content-type.
Something like this:
var headersOptions = {
method: "HEAD",
host: "http://example.com",
path: "/articles/content.html"
};
var req = http.request(headersOptions, function (res) {
// you will probably need to also do things like check
// HTTP status codes so you handle 404s, 301s, and so on
if (res.headers['content-type'].indexOf("text/html") > -1) {
// do something like queue the link up to be crawled
// or parse the link or put it in a database or whatever
}
});
req.end();
One benefit is that you only grab the HEAD, so even if the file is a gigantic video or something, it won't clog things up. You get the HEAD, see the content-type is a video or whatever, then move along because you aren't interested in that type.
Second, you don't have to keep track of file names because you're using a standard MIME type to differentiate html from other data formats.

node.js/Jade - How to pre-compile jade files and cache it?

Framework: node.js / express.js / Jade
Question: in production env, when a jade file is rendered by express, jade cache's it so future renders are faster.
When I start node.js app, how can I pre-compile (or) pre-render (like warmup) all the jade files so its already in cache when requests start to come in...
I can use a folder recursion, I just need to know how to pre-compile (or) pre-render.
Is this possible?
Jade has template pre-compiling and caching built in.
http://jade-lang.com/api/
Simply specify cache: true option to jade.compileFile, and iterate through all of your template files.
var options = {cache: true};
// iterate/recurse over your jade template files and compile them
jade.compileFile('./templates/foo.jade', options);
// Jade will load the compiled templates from cache (the file path is the key)
jade.renderFile('./templates/foo.jade');
If you're not using any parameters, you can compile jade templates directly to HTML with grunt or gulp and make it watch for file modifications
Try it from the command-line:
jade view/map-beacons.jade -D
If you do need to use parameters I would use something like in Andrew Lavers answer.
compileFile returns a function which you can use to pass in parameters i.e. fn({ myJsVar: 'someValue' })
There is also a client option in the command-line but I didn't find any use for it :
jade view/map-beacons.jade -cD
i do this solution, this code outside http.createServer function
let cache_index=jade.renderFile('index.jade');
and when return view
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
res.end(cache_index);
when use this solution server return index to 1ms
but without solution server return index between 150ms to 400ms
result:
Picture 1 with cache
Picture 2 without cache

Is it possible to use PhantomJS and Node to dynamically generate PDFs from templates?

Background / Need
I am working with a group on a web application using Node.JS and Express. We need to be able to generate reports which can be printed as hard copies and also hard copy forms. Preferably we'd like to dynamically generate PDFs on the server for both reports and hand written forms. We're currently using EJS templates on the server.
Options
I was thinking that it would be convenient to be able to use templates to be able to build forms/reports and generate a PDF from the resulting HTML, however my options to do this appear limited as far as I can find. I have looked at two different possible solutions:
PhantomJS -- (npm node-phantom module)
PDFKit
EDIT: I found another Node.JS module which is able to generate PDFs from HTML called node-wkhtml which relies on wkhtmltopdf. I am now comparing using node-phantom and node-wkhtml. I have been able to generate PDFs on a Node server with both of these and they both appear to be capable of doing what I need.
I have seen some examples of using PhantomJS to render PDF documents from websites, but all of the examples I have seen use a URL and do not feed it a string of HTML. I am not sure if I could make this work with templates in order to dynamically generate PDF reports.
When a request for a report comes in I was hoping to generate HTML from an EJS template, and use that to generate a PDF. Is there anyway for me to use Phantom to dynamically create a page completely on the server without making a request?
My other option is to use PDFkit which allows dynamic generation of PDFs, but it is a canvas-like API and doesn't really support any notion of templates as far as I can tell.
The Question
Does anyone know if I can use PhantomJS with Node to dynamically generate PDFs from HTML generated from a template? Or does anyone know any other solutions I can use to generate and serve printable reports/forms from my Node/Express back-end.
EJS seems to run fine in PhantomJS (after installing the path module). To load a page in PhantomJS given a string of HTML, do page.content = '<html><head>...';.
npm install ejs and npm install path, then:
var ejs = require('ejs'),
page = require('webpage').create();
var html = ejs.render('<h1><%= title %></h1>', {
title: 'wow'
});
page.content = html;
page.render('test.pdf');
phantom.exit();
(Run this script with phantomjs, not node.)
I am going to post an answer for anyone trying to do something similar with node-phantom. Because node-phantom controls the local installation of PhantomJS, it must use asynchronous methods for everything even when the corresponding PhantomJS operation is synchronous. When setting the content for a page to be rendered in PhantomJS you can simply do:
page.content = '<h1>Some Markup</h1>';
page.render('page.pdf');
However, using the node-phantom module within node you must use the page.set method. This is the code I used below.
'use strict';
var phantom = require('node-phantom');
var html = '<!DOCTYPE html><html><head><title>My Webpage</title></head>' +
'<body><h1>My Webpage</h1><p>This is my webpage. I hope you like it' +
'!</body></html>';
phantom.create(function (error, ph) {
ph.createPage(function (error, page) {
page.set('content', html, function (error) {
if (error) {
console.log('Error setting content: %s', error);
} else {
page.render('page.pdf', function (error) {
if (error) console.log('Error rendering PDF: %s', error);
});
}
ph.exit();
});
});
});
A really easy solution to this problem is the node-webshot module - you can put raw html directly in as an argument and it prints the pdf directly.

Resources