Limit express-busboy to specific routes - node.js

I set up file uploads with express-busboy using the example from the repository here
which doesn't seem to use the normal use() syntax so I'm a little confused as to how to actually limit this middleware so it only executes on a specific route because it's breaking other POST requests.
This is how I configured it:
var busboy = require('express-busboy');
busboy.extend(app, {
upload: true,
path: './uploads/temp'
});

In allowedPath value you can specify regex in this case limit at post route defined in express application. likes /uploads
busboy.extend(app, {
upload: true,
path: './uploads/temp',
allowedPath: /^\/uploads$/
});
or other wise you can pass function
var options = {
upload: true,
path: './uploads/temp',
};
options.allowedPath = function(url) {
return url == '/api/ccUpload';
}
busboy.extend(app, options);

Well since express-busboy wasn't working for me, I tried using express-fileupload instead and that seems to work now.

Try using Multer instead, and limit it to your route:
app.post('/^\/api\/ccUpload$/',
multer({
dest: './uploads/temp',
rename: function(fieldname, filename, req, res) {
return filename.toLowerCase();
}
}),
yourRouteHandler
);

Related

Send Blob File from html form to express server so it can be uploaded to cloud

So I'm trying to make the html form:
<form action="blahblah" encblah="multipart/form-data" whatever>
Thats not the problem, I need to make that form send the blob to express
app.post('/upload/avatars', async (req, res) => {
const body = req.body;
console.log(req.file);
console.log(body);
res.send(body);
});
So I can access the blob, create a read stream, pipe it to the cloud, and bam, upload the file without downloading anything on the express server it self.
Is that possible?
If yes, please tell me how.
If no, please tell me other alternatives.
On the client we do a basic multi-part form upload. This example is setup for a single image but you could call uploadFile in sequence for each image.
//client.ts
const uploadFile = (file: File | Blob) => {
const formData = new FormData();
formData.append("image", file);
return fetch("/upload", {
method: "post",
body: formData,
});
};
const handleUpload = (event: any) => {
return event.target.files.length ? uploadFile(event.target.files[0]) : null;
};
On the server we can use multer to read the file without persisting it to disk.
//server.js
const express = require("express");
const app = express();
const multer = require("multer");
const upload = multer();
app.post(
"/upload",
upload.fields([{ name: "image", maxCount: 1 }]),
(req, res, next) => {
console.log("/upload", req.files);
if (req.files.image.length) {
const image = req.files.image[0]; // { buffer, originalname, size, ...}
// Pipe the image.buffer where you want.
res.send({ success: true, count: req.files.image.originalname });
} else {
res.send({ success: false, message: "No files sent." });
}
}
);
For larger uploads I recommend socket.io, but this method works for reasonably sized images.
it is possible, but when you have a lot of traffic it would overwhelm your express server (in case you are uploading videos or big files ) but if it's for uploading small images (profile image, etc...) you're fine. either way you can use Multer npm
I'd recommend using client-side uploading on ex: s3-bucket, etc..., which returned a link, and therefore using that link.

How to redirect on get request to static image asset with Express

I am trying to create a redirect after a get request to a static image asset using something like this but I can't figure it out.
Can anyone give me an idea?
app.get('/pixel-tracker.gif', function(req, res, next) {
var options = {
root: path.join(__dirname, 'public'),
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
}
var fileName = req.params.name;
res.sendFile(fileName, options, function (err) {
if (err) {
next(err)
} else {
console.log('Sent:', fileName);
res.redirect('/pixel-parser');
}
});
})
Calling res.redirect after res.sendFile doesn't make sense. By calling res.sendFile you're responding with the file, i.e. you've already responded to the client and try to send another redirect response. You need to either send a file or redirect, can't do both.
I figured this out.
You can't set an app.post to fire on a route where you have a public asset. I removed the gif from the public folder and it works perfectly now.

How do I upload images using MongoDB + Node.js (Express.js)?

I also use Mongoose, if that is relevant. I am trying to allow users to upload a profile picture. There must be a simple way, isn't there?
I think you should try multer.
Simple from multer site:
var multer = require('multer')
var upload = multer({ dest: 'uploads/' })
app.post('/upload', uploads.any(), function(req,res){
res.send(req.files);
});
It should upload file in your uploads folder (under root), and return file in JSON.
In this example you will see how to store the file you are sending in to your server directory and then pick them up from there and save them. You can also directly save them. First you pick up the file using angular, you can use anything, if you want you can check here for more details. Here is my small example the code is in jade.
<input type="file" name="file" onchange="angular.element(this).scope().selectFile(this.files)"/>
<button ng-click="savePhoto()">Save </button>
In your angular controller
$scope.savePhoto = function () {
var fd = new FormData();
fd.append("file", $scope.files[0]);
)) ;
$http.post("/xxx/photos", fd, {
withCredentials: true,
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
}).success(function (data) {
$scope.image = data; // If you want to render the image after successfully uploading in your db
});
};
Install multer using npm in your back end. And then in app.js you can set up a middleware to collect the files you are sending in. Just do console.log(req) here to check if you are getting the files till here. Multer does the magic here.
app.use(multer({
dest: path.join(__dirname, 'public/assets/img/profile'),
rename: function (fieldname, filename, req, res) {
console.log(req)// you will see your image url etc.
if(req.session.user) return req.session.user.id;
}
}));
So here the image will be stored in this path (public/assets/img/profile) in your server. Now you pick up the file from this server and add to your db.
var path = require('path');
var imgPath =path.join(__dirname, '../public/assets/img/profile/' + id + '.jpg'); // this is the path to your server where multer already has stored your image
console.log(imgPath);
var a ;
a = fs.readFileSync(imgPath);
YourSchema.findByIdAndUpdate( id, {
$set:
{'img.data' : a,
'img.contentType' : 'image/png' }
}, function(err, doc) {
if (err)console.log("oi");
}
);
//In case you want to send back the stored image from the db.
yourSchema.findById(id, function (err, doc) {
if (err)console.log(err);
var base64 = doc.img.data.toString('base64');
res.send('data:'+doc.img.contentType+';base64,' + base64);
});

Uploading images with fetch to Express using Multer

I'm attempting to upload images to an Express server. I'm not exactly sure about how to do this, but heres what I've got from what I've been able to pick up from MDN, express, react-dropzone, and multer Documentation. Multer does not seem to pick up the FormData object from react-dropzone, when logging out req.file it returns undefined.
server.js
var storage = multer.diskStorage({
destination: './public/users',
filename: function (req, file, cb) {
switch (file.mimetype) {
case 'image/jpeg':
ext = '.jpeg';
break;
case 'image/png':
ext = '.png';
break;
}
cb(null, file.originalname + ext);
}
});
var upload = multer({storage: storage});
app.use(upload.single('photo'));
app.post('/uploadUserImage', function (req, res) {
console.log(JSON.stringify(req.body.photo)) // form fields
console.log(req.photo) // form files
console.log(req.file) // form files
res.send(req.body.photo);
});
client.js
function uploadImage (image) {
var formData = new FormData();
formData.append('photo', image);
fetch('http://localhost:8080/uploadUserImage/', {
method:'POST',
body: formData
});
}
When I make this request morgan logs out the following:
{ photo: '[object File]' } <--- from console.log(req.body');
undefined <--- from console.log(req.file);
multer creates the folder public/uploads but does not place the image in the location. How can I get the photo because I need to run it through sharp (to compress the filesize and resize the image) and then place it in the uploads folder?
The error occurs because you specified the 'Content-type' explicitly. To do this properly, you'd also need to specify the boundary. You can find a detailed explanation of multipart/form-data here: How does HTTP file upload work?
To solve the issue with the file upload, you should remove the 'Content-Type' specification from the fetch request. You can also refactor the uploadImage method to upload the form without parsing the inputs:
function uploadImage () {
// This assumes the form's name is `myForm`
var form = document.getElementById("myForm");
var formData = new FormData(form);
fetch('http://localhost:8000/uploadUserImage', {
method: 'POST',
body: formData
});
}
The problem for me was that firebase has an error and can't use multer. You need to use busboy and parse it manually. Also I needed to append the uri from react native imagePicker instead of the file blob. Like this:
data.append('fileData', {
uri : pickerResponse.uri,
type: pickerResponse.type,
name: pickerResponse.fileName
});

how to generate a sitemap in expressjs

I downloaded my XML sitemap from the sitemap xml generator website. I placed my sitemap.xml on my public directory but when I tried to submit the sitemap.xml into google console i received the following error: General HTTP error: 404 not found
HTTP Error: 404So i codedapp.get('/sitemap.xml', function( req, res, next ) {
res.header('Content-Type', 'text/xml');
res.render( 'sitemap' );
)};And when i navigate to the 'website/sitemap.xml' I am getting the following error: This page contains the following errors:
error on line 1 at column 42: Specification mandate value for attribute itemscope
Thanks for your help
Generate your sitemap.xml file using a tool like https://www.xml-sitemaps.com/
upload the sitemap.xml in your project
then add this to your .js file:
router.get('/sitemap.xml', function(req, res) {
res.sendFile('YOUR_PATH/sitemap.xml');
});
make sure you change YOUR_PATH for the actual path where your sitemap.xml file is.
Sitemaps do not have to be XML documents. A simple text file with URLs is all you need so something like below works fine. In the following example, fetchMyUrls() would be a function/method that asynchronously gets and returns the available URLs as an array of strings (URL strings).
async function index (req, res){
return fetchMyUrls().then((urls) => {
var str = '';
for (var url of urls) {
str = str + url + '\n';
}
res.type('text/plain');
return res.send(str);
});
}
For those looking for a way to create the XML dynamically on your code and don't want to use another library nor have a file stored in the public folder, you can use this:
app.get('/sitemap.xml', async function(req, res, next){
let xml_content = [
'<?xml version="1.0" encoding="UTF-8"?>',
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
' <url>',
' <loc>http://www.example.com/</loc>',
' <lastmod>2005-01-01</lastmod>',
' </url>',
'</urlset>'
]
res.set('Content-Type', 'text/xml')
res.send(xml_content.join('\n'))
})
In my NodeJS express project and without installing any library I was able to add this to my routes with my preferred view engine (handlebar).
export const routes: RouteMapper[] = [
{
"/sitemap.xml": [
{
method: "get",
handler: (req, res) =>
res.sendFile("/src/views/sitemap.xml", { root: "." }),
},
],
},
];
Cheers!
The best way is to create a script that would automatically generate a sitemap. In a lot of cases, the URLs should be dynamic based on data from the database.
Great package for creating the sitemap in Express is sitemap package:
STEP 1
Create a middleware that will generate the sitemap dynamically and then cache it for each next call to the server. We can extract logic in separate file called sitemap_generator.js for example, and we can define and export generate_sitemap middleware for it:
const { SitemapStream, streamToPromise } = require('sitemap');
const { Readable } = require('stream');
let sitemap;
const generate_sitemap = async (req, res, next) => {
res.header('Content-Type', 'application/xml');
if (sitemap) return res.status(200).send(sitemap); // If we have a cached entry send it
let changefreq = 'weekly';
try {
let links = [
{ url: '', changefreq, priority: 1 },
{ url: 'aboutus', changefreq, priority: 0.9 },
{ url: 'blog', changefreq },
{ url: 'login', changefreq },
{ url: 'register', changefreq },
];
// Additionally, you can do database query and add more dynamic URLs to the "links" array.
const stream = new SitemapStream({ hostname: 'https://example.com', lastmodDateOnly: true })
return streamToPromise(Readable.from(links).pipe(stream)).then((data) => {
sitemap = data; // Cache the generated sitemap
stream.end();
return res.status(200).send(data.toString())
});
} catch (error) {
return res.status(500).end();
}
}
module.exports = { generate_sitemap };
STEP 2
Import generate_sitemap middleware from sitemap_generator.js in your server configuration file and mound it to the /sitemap.xml endpoint:
const { generate_sitemap } = require('./sitemap_generator');
...
app.get('/sitemap.xml', generate_sitemap);
That's it. Your sitemap should be available on /sitemap.xml endpoint now so navigate in the browser to that endpoint and check if it is there.

Resources