How do you specify file upload directory for express within sails? - file-upload

I'm uploading a file from a browser <input type="file" name="myfile" id="myfile" using sails. I need to place it in other than the default express location. I'd use the following code in naked Express:
app.use(express.bodyParser({ keepExtensions: true, uploadDir: uploadFolder }))
For sails I wrote code along the lines of this relevant answer
First, I tried SkyTecLabs' answer. policies/configfileupload.js contains
'use strict';
var sailsExpress = require('../../node_modules/sails/node_modules/express'),
path = require('path');
console.log('.. initializing policies/configFileUpload');
module.exports = function configFileUpload (req, res, next) {
var uploadFolder = path.normalize(__dirname + '/../public/uploads');
console.log('.. in policies/configfileupload.js. uploadFolder=', uploadFolder);
console.log('typeofs are=',typeof req, typeof res, typeof next, typeof sailsExpress);
sailsExpress.bodyParser({ keepExtensions: true, uploadDir: uploadFolder });
next();
};
config/policies.js contains
'SchedController' : {
'uploadsubmit': 'configfileupload'
}
Express continues to upload the file to the default directory. Typeof req, res, next, sailsexpress are: object, object, function, function so the signature looks OK. (I tried returning the function configFileUpload just in case, but the controller was never called.)
Then I tried mikemcneil's suggestion. config/express.js
'use strict';
var sailsExpress = require('../node_modules/sails/node_modules/express'),
path = require('path');
module.exports.express = {
customMiddleware: function (app) {
var uploadFolder = path.normalize(__dirname + '/../public/uploads');
console.log('.. in config/express.js. uploadFolder=', uploadFolder);
console.log('typeof sailsExpress=', typeof sailsExpress, 'typeof app=', typeof app);
app.use(sailsExpress.bodyParser({ keepExtensions: true, uploadDir: uploadFolder }));
}
};
and the upload was still placed in the default directory. The typeofs sailsexpress and app are both function.
If the above code is correct for including express middleware in sails, then perhaps this middleware is performed only after express' formidable dependency parses the data and uploads the file.
I don't know in this case of any place in sails where I can place express configuration options such as bodyParser.

Warning: Before you release your app...
If you're expecting lots of file uploads, and especially big ones, you need to reconsider your strategy before deploying into production. You can replace the bodyParser middleware manually by replacing sails.config.express.bodyParser.
The default bodyParser in Express/Connect (and Sails, as of v0.9.x) uses a tmp directory to buffer files (similar to what you'd see in PHP). To get uploaded files somewhere else, you'll need to move them. Check out http://howtonode.org/really-simple-file-uploads for more on how to do that.
There is another option you might check out, depending on how adventurous you are. As I have time, I've been working on an alternative default bodyParser that can be used with Sails or Express. It defers to the underlying library for param/JSON parsing, but uses formidable's raw onPart events to allow streaming uploads without writing the entire file to disk. More on that:
Example repo: https://github.com/mikermcneil/stream-debug
Discussion: https://groups.google.com/d/msg/sailsjs/525fK7pgK8U/bduPudCSLUgJ
Source: https://github.com/mikermcneil/file-parser

Related

Express multer - disallow file uploads except for specific routes?

Well currently I am disallowing all file uploads to routes by setting up the server like:
const upload = multer();
const server = express();
module.exports = () => {
// ...
server.use(logger('dev'));
server.use(express.json());
server.use(express.urlencoded({ extended: false }));
server.use(express.raw());
server.use(cookieParser());
server.use(express.static(path.join(projectRoot, 'public')));
server.set('trust proxy', 1);
server.use(upload.none());
server.use('/', router);
// ...
}
Which correctly blocks all files. Now I wish to allow uploading files only in the POST request to /test:
import * as express from "express";
import multer from "multer";
const upload = multer({storage: multer.memoryStorage()});
const router = express.Router();
router.post('/test', upload.single('pdf'), function(req, res, next) {
const r = 'respond with a test - POST';
res.send(r);
});
However when I try to use this in postman I get the error "multerError", "LIMIT_UNEXPECTED_FILE" for the field 'pdf'. I notice that if I remove the line server.use(multer.none()) it works, but then I can upload files to any place anyways, not exactly what I like?
Nothing will be uploaded to your server unless you specify a multer middleware on the entire server, on a route, or on a particular path. So you can safely remove the server.use(upload.none());.
The middleware will then not try to consume the payload of the incoming request. How much load the receiving (without consumption) of the payload causes on the server, I don't know, but you could theoretically destroy the connection whenever the client tries to submit a payload:
req.on("data", function() {
req.destroy();
});
But perhaps the creation of new connection afterwards causes more load on the server overall.

Express is serving files without going through route handlers?

I have the following code:
const app = express();
app.use(express.static(path.resolve('../frontend/dist')));
const server = http.createServer(app);
app.get('/', (req, res) => {
console.log('received request');
res.sendFile(path.resolve('../frontend/dist/index.html'));
});
If I comment out the app.get handler, index.html is served at localhost:3000 anyway, apparently due to the second line. The get handler is not actually executing - received request is never printed in the console.
But without the second line, it can't serve static assets and JS & CSS fail to load. What am I doing wrong here? Why is the static asset config causing the route to be ignored?
express.static() is a generic route handler. It tries to match incoming requests to specific files at a target location in your file system. One of the special features it has is that if it gets a request for / it will look for index.html in the target directory.
You can see the feature described in the doc and, in fact, there's an option { index: false} that can turn the feature off if you don't want it to serve index.html automatically.

Serving static files from an express/node.js application

Hi I am a newbie and started to learn about node recently. I took an Heroku tutorial on websockets (https://devcenter.heroku.com/articles/node-websockets) and adapted it for a specific project I was working on. In the example code there was a single index.html file with some embedded javascript. I moved this script out to a separate file and referenced it in the HTML. Everything worked fine locally but doesn't work when i deploy to Heroko. I chatted with the very helpful team at Heroku who informed me that my server side code is serving up all files as HTML and I need to change the code. They gave me some pointers and I tried as many things as I could over several days but to no avail. In the end they recommended coming to this forum as a way to solve the problem as it is beyond their scope. The existing code that serves up the index.html file is as follows:
const express = require('express');
const SocketServer = require('ws').Server;
const path = require('path');
const PORT = process.env.PORT || 3000;
const INDEX = path.join(__dirname, 'index.html');
const server = express()
.use((req, res) => res.sendFile(INDEX) )
.listen(PORT, () => console.log(Listening on ${ PORT }));
At first i edited this to include the line:
app.use(express.static('public'))
but this didn't work. I then amended as follows and it still doesn't work:
const INDEX = path.join(__dirname, 'index.html');
const JS = path.join(__dirname, 'client.js');
const server = express()
.use((req, res) => {
res.sendFile(INDEX);
res.sendFile(JS);
I have looked at other tutorials that work when i run them in isolation but when I try to adapt my above code it simply doesn't work. I would really appreciate if someone out there could point me in the right direction.
BTW this is what Heroku told me:
"To explain a bit further this error Uncaught SyntaxError: Unexpected token < is because the URL for http://thawing-journey-33085.herokuapp.com/client.js isn't serving a javascript file but is instead trying to serve the HTML for the homepage. This suggests you have an issue with the routing in your application which you'll need to review. This is probably because your server.js file doesn't check for any particular URL before sending the index.html file."
Thanks
I serve my static files like this:
// define the folder that will be used for static assets
app.use(Express.static(path.join(__dirname, '../public')));
// handle every other route with index.html, which will contain
// a script tag to your application's JavaScript file(s).
app.get('*', function (request, response){
response.sendFile(path.resolve(__dirname, '../public', 'index.html'));
});
This way i set the static folder in the express.static middleware so i can serve the files. And then i redirect all url request to the index.html
To know more: express static

Can't figure out Parse Hosting - Cloud Code Integration

I've been working on this seemingly simple problem for about a week now and feel like there is conflicting information and am hoping someone can give shed some light for me. I'm trying to use Parse Hosting for a marketing site with bootstrap, just HTML and CSS with a little JS; and Cloud Code to do some simple server side tasks like charging a card via Stripe. Everything in the documentation makes it seem this is easily doable, but the documentation also seems to lead me to believe certain methods aren't.
For example, this video shows a Stripe engineer building exactly what I want. However, it's not abundantly clear that he is using pure HTML and CSS for the front end instead of an Express templating engine (which I am not using) - http://blog.parse.com/videos/parse-developer-day-2013-a-new-kind-of-checkout/
This post says Parse Hosting and Express now work hand in hand, GREAT!
http://blog.parse.com/announcements/building-parse-web-apps-with-the-express-web-framework/
But the documentation (JS > Cloud Hosting > Dynamic Websites) says you have to delete index.html >> "If you choose to use Express or Node.js, you'll first need to delete public/index.html so that requests can get through to your custom handler functions."
I want to have a single page website hosted at public/index.html that uses Stripe Checkout v3 to create a token then pass that to Parse for a quick execution of the charge, but again, every which way I try has been unsuccessful so far.
In addition, I'm thinking Parse Hosting of pure HTML/CSS won't work with Cloud Code the way I want because a simple call of /hello below returns nothing.
Here's my code:
//public
//index.html
<form action="/charge" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="pk_test_zippitydoo"
data-image="http://image.jpg"
data-name="Thing"
data-description="Shut up and take my money"
data-amount="4000">
</script>
</form>
//cloud
//main.js
var express = require('express');
var app = express();
var Stripe = require('stripe');
Stripe.initialize('sk_test_blahblahblah');
app.get('/hello', function(req, res) {
res.send('hello world');
});
app.post('/charge', function(req, res) {
res.send('Charge Attempt');
token_id = req.body.stripe_token
Stripe.Tokens.retrieve(token_id).then(function(token) {
return Stripe.Charges.create({
amount: 1000,
currency: "usd",
source: token_id
});
});
});
What you need is for express to serve your HTML. To do this, register a static resources directory. In your main.js, after you instantiate your app with var app = express(), do this:
app.use(express.static('public'));
Express should then treat your /public/index.html file as the directory index by default, and your app will serve any other files under /public. More info: http://expressjs.com/4x/api.html#express.static
There are several things I did wrong here. I'll explain my mistakes, then you can compare the below code that works with the above code in the question that doesn't.
1) I wasn't parsing the data I was receiving (see underneath // App configuration section)
2) The JSON that is passed needs to be parsed using CamelCase (stripeToken not stripe_token)
3) The charge is set as a variable, not returned (var = charge instead of return charge). Return may work, I didn't test it however.
4) It is imperative that you include the app.listen(); in order to connect to the public folder from the cloud folder
//cloud
//main.js
var express = require('express');
var Stripe = require('stripe');
Stripe.initialize('sk_test_blahblahblah');
var app = express();
// App configuration section
app.use(express.bodyParser()); // Middleware for reading request body
app.post('/charge', function(req, res) {
var stripeToken = req.body.stripeToken;
var stripeEmail = req.body.stripeEmail;
res.send('Charging your card...');
var charge = Stripe.Charges.create({
amount: price,
currency: "usd",
source: stripeToken,
receipt_email: stripeEmail
}, function(err, charge) {
if (err && err.type === 'StripeCardError') {
res.send('The card has been declined. Please check your card and try again.');
}
});
});
// Attach the Express app to your Cloud Code
app.listen();

How to make an Express site without a template engine?

I do not want Jade or EJS on my site. How can I create an express site without it defaulting to the Jade templates? Thanks
If what you want is directly to serve static html files with the possibility to cache resources, while still being able to hit "/" and get index.html, then the answer is as easy as this:
var express = require('express');
var http = require('http');
var app = express();
app.use(express.static(__dirname + '/public'));
http.createServer(app).listen(3000);
Gotcha: Html files including index.html must be inside /public folder instead of /views
You could use commands below to install express-generator globally and then scaffold a project without a view engine
npm install -g express-generator
express newProject --no-view
You can comment out the lines
app.set 'views', __dirname + '/views'
app.set 'view engine', 'jade'
from the Express initialization code.
If you are serving only static content: https://github.com/visionmedia/express/blob/master/examples/static-files/index.js
Otherwise, use your database, your files, your user input, or whatever to concatenate a string that will make up the http response.
// Express 3.x
app.get('*', function(req,res){
fs.readFile('./foo.txt', 'utf8', function (err, data) {
if (err) throw err;
data += (req.query['something'] || "")
res.type('text/plain');
res.send(200, data);
});
});
With that said: I have grown to love Jade as I've been playing with it for the past few months. It has its idiosyncracies but it's orders of magnitude faster to write any complicated html.
With Express 4.0.0, the only thing you have to do is comment out 2 lines in app.js:
/* app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade'); */ //or whatever the templating engine is.
And then drop your static html into the /public directory. Example: /public/index.html
Use Restify
http://restify.com/
var restify = require('restify'),
fs = require('fs');
var server = restify.createServer({
certificate: fs.readFileSync('path/to/server/certificate'),
key: fs.readFileSync('path/to/server/key'),
name: 'MyApp',
});
server.listen(8080);
It borrows heavily from Express -Routing