How do I upload images using MongoDB + Node.js (Express.js)? - node.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);
});

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.

nodejs: Retrieving base64 Images from Mongodb using Postman

Looking for help on Uploading and Retrieving Images from MongoDb using multer.
My front end is ReactNative.(Not sure if this is needed but just to be sure.)
Multer
Problem: After looking and following tutorials i'm able to encode my path to base64 and upload it to my DB but now i'm confused how to retrieve the file from my DB. I saw some tutorials about decoding it from base64 but I don't quite understand how do I go about retrieving an image and displaying it in postman. (I tried looking but haven't found anything that gives me an answer. I'm sorry if this is a duplicated question. If you could point me in a direction or give me some advice I would be really greatful.)
**POST**
route.post("/sad", upload.single("image"), (req, res, next) => {
console.log(req.file);
const img = fs.readFileSync(req.file.path);
const img_enc = img.toString('base64');
const obj = {
usrImage: {
data: new Buffer.from(img_enc, 'base64'),
contentType: "image/jpg",
},
};
console.log(obj);
const newAccout = new account(obj);
newAccout.save();
});
**RETRIEVE**
route.get('/sad',(req,res)=>{
img.find({}).then((img)=>{
res.json(img)
//How do decode my buffer to show an image in Postman?
})
}
)
I am trying to create a userprofile where a username,password and image is saved. If you can help save an Image and then retrieve it from my accounts collection.
Hey I would advise that you start using a 3rd party for file upload like cloudinary very good way of managing files i.e images or video...
I am not that well of with multer but I can give a quick code example using Formidable does the same work as multer
Before you can start you'd need to make an account on cloudinary.com(don't worry its free)
Code below is how you could handle file upload
const Formidable = require("formidable"); //Meant for body parsing
const cloudinary = require("cloudinary").v2; // file uploader
//This below is your connection/configuration to get access to your cloudinary account so cloud_name, api_key and api_secret you'll get in your home dashboard(Cloudinary)
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET,
});
router.post('/api/file-upload', (req, res)=>{
const form = new Formidable.InconmingForm();
form.parse(req, (error, fields, files)=>{
const {file} = files
cloudinary.uploader.upload(file.path, {folder:"/"}, (err, res)=>{
const file_url = res.secure_url //This would be the url for your file given back by cloudinary
})
})
})
This script should upload your file and the file_url will be having the url of the file that you upload having ssl then after that you can now continue saving to mongoDB
Cloudinary docs for NodeJS
https://cloudinary.com/documentation/node_integration
Nice clear and understandable docs
Shameless plug
If you get lost you can check this video out on YouTube that I made handling file upload with cloudinary then save url given back to mongoDB
https://youtu.be/mlu-tbr2uUk
First call api find one
you will need fs module to complete following query
const fs = require('fs');
let data = await db.user.findOne({
where: {
id = req.body.id
}
})
// _________________ base 64 string data from findone query data
// |
let buff = new Buffer(data.image, 'base64');
let name = name.jpeg
let path = `tmp/${name}`; // <--- destination and file name you want to give to your file
fs.writeFileSync(path, buff);// < --this will write file to given path
fs.readFile(path, function (err, content) {// <------to send file in postman response
if (err) {
res.writeHead(400)
console.log(err);
res.end("No such image");
} else {
//specify the content type in the response will be an image
res.writeHead(200);
res.end(content);
}
});
fs.unlink(path, (err) => { // <-----to delete file from tmp directory
if (err) {
console.log(err)
}
})
Try this and switch to preview tab in postman.
I haven't tried it but maybe it helps.
route.get('/sad',(req,res)=>{
img.find({}).then((img)=>{
res.setHeader('contentType','image/jpg').send(img)
})
})

Firebase storage: Image file types not showing after upload. How do I use the image?

When I use an npm package like google-cloud or multer-gcs to upload a file to firebase storage bucket (that uses google cloud storage under the hood..) the file gets uploaded. However, the file does not show and image type(MIME). Also, how do I use the image in my node.js application?
Here is the code to upload image to firebase bucket. Used multer to handle file upload..
var path = require('path');
var multer = require('multer');
var gcs = require( 'multer-gcs' );
var storage = gcs({
filename : function( req, file, cb ) {
cb( null, file.fieldname + '-' + Date.now() + path.extname(file.originalname).toLowerCase());
},
bucket : 'p****n-bcXX3.appspot.com', // Required : bucket name to upload
projectId : 'p****n-bcXX3', // Required : Google project ID
keyFilename: './p****n-5cbc725XXXXd.json', // Required : JSON credentials file for Google Cloud Storage
acl : 'publicread' // Optional : Defaults to private
});
In my routes:
router.post('/food/create', [authChecker, gcsUpload.single('food_image')], function(req, res, next) {
Controllers.create_food(req, res);
});
When I upload file directly to the bucket, type shows clearly. What's the catch here?
It looks like multer-gcs doesn't support the ability to add custom metadata to a file. If you use gcloud directly, you'd do something like this:
var options = {
destination: 'new-image.png',
metadata: {
contentType: 'image/png'
}
};
bucket.upload('local-image.png', options, function(err, file) {
// Your bucket now contains:
// - "new-image.png" (with the contents of `local-image.png')
// `file` is an instance of a File object that refers to your new file.
});
I'd file an issue with the developer of multer and ask them to support metadata uploads along side blob uploads, especially since gcloud already supports the ability to do this.
Here is what I am doing now to handle the file upload.
req.file.modifiedname = (Math.random().toString(36)+'00000000000000000').slice(2, 10) + Date.now() + path.extname(req.file.originalname);
var options = {
metadata: {
contentType: req.file.mimetype
},
predefinedAcl : 'publicread'
};
var blob = bucket.file(req.file.modifiedname);
var blobStream = blob.createWriteStream(options);
blobStream.on('error', function (err) {
return next(err);
});
blobStream.on('finish', function() {
publicUrl = format(
'https://storage.googleapis.com/%s/%s',
bucket.name, blob.name
);
req.file.gcsfileurl = publicUrl;
console.log(req.file.gcsfileurl);
next();
});
blobStream.end(req.file.buffer);

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

mongodb gridfs encoding picture base64

i try to readout an image, saved in mongodb, via gridfs (without temporary file)
it should be directly sent to ajax, which injects it into html
when i use my actual functions a large bit string is formed and sent to client (is saved in ajax response var)
but as it reaches the client, the bits arent correct anymore
so i look for a way to encode the picture before it is sent (into base64)
(or is there any other way?)
Serverside - javascript, gridfs
exports.readFileFromDB = function(req, res, profile, filename, callback){
console.log('Find data from Profile ' + JSON.stringify(profile));
var GridReader = new GridStore(db, filename,"r");
GridReader.open(function(err, gs) {
var streamFile = gs.stream(true);
streamFile.on("end", function(){
});
// Pipe out the data
streamFile.pipe(res);
GridReader.close(function(err, result) {
});
Clientside - javascript ajax call:
function imgUpload(){
var thumb = $("#previewPic");
$('#uploadForm').ajaxSubmit({
beforeSend:function(){
//launchpreloader();
},
error: function(xhr) {
//status('Error: ' + xhr.status);
},
success: function(response) {
console.log(response);
var imageData = $.base64Encode(response);
console.log(imageData);
thumb.attr("src","data:image/png;base64"+imageData);
$("#spanFileName").html("File Uploaded")
}
});
}
I'm doing something similar for a current project, but when the upload is complete, I return a JSON object containing the URL for the uploaded image:
{ success : true, url : '/uploads/GRIDFSID/filename.ext' }
I have a route in Express that handles the /uploads route to retrieve the file from GridFS and stream it back to the client, so I can use the above URL in an IMG SRC. This is effectively what appears in the DOM:
<img src="/uploads/GRIDFSID/filename.ext">
The route handler looks something like this (it uses node-mime and gridfs-stream):
app.get(/^\/uploads\/([a-f0-9]+)\/(.*)$/, function(req, res) {
var id = req.params[0];
var filename = req.params[1];
// Set correct content type.
res.set('Content-type', mime.lookup(filename));
// Find GridFS file by id and pipe it to the response stream.
gridfs
.createReadStream({ _id : id })
.on('error', function(err) {
res.send(404); // or 500
})
.pipe(res);
});
It obviously depends on your exact setup if my solution works for you.

Resources