How to display images from mongoDB gridfs in react/html? - node.js

I'm new to NodeJS and express and recently figured out how to save images in mongodb with the gridfs system and mongo native nodejs driver. Now, I want to display the images I downloaded from mongo gridfs. I tried two methods:
//getProfilePicture route function
exports.getPfp = async function(req, res, next){
//retrieve user from auth middleware with mongoose
const user = await User.findOne({uniqueID: req.user.uniqueID});
const profilePicture = user.profile.profilePicture[0];
//profilePicture.payload is the filename with which the file in the fs.files collection is saved
console.log(profilePicture.payload)
if(profilePicture.type='pfpUploadSubmit')
{ client.connect(function(err) {
assert.equal(null, err);
console.log("Connected successfully to server");
const db = client.db("BookProject");
const bucket = new mongodb.GridFSBucket(db);
const dlpath=path.join(__dirname, '../public/image.jpeg')
//here im writing the file from gridfs to a local node directory
bucket.openDownloadStreamByName(profilePicture.payload).
pipe(fs.createWriteStream(dlpath)).
on('error', function(error) {
assert.ifError(error);
}).
on('finish', function() {
console.log('done!');
process.exit(0);
})
})
}
}
Another method is instead of the
.pipe(fs.createWriteStream(dlpath))
I simply write
.pipe(res)
the former succesfully save the image on my nodeJS local directory, but how do I allow reactjs client to access it?
The the second sends the stream in the response object. When I console.log the response, I get what seems like an unreadable data stream and I have no idea how to turn it into an image to render onto my react component.
What is the best way to do this in production?
Thanks from a newbie

Related

If I store my video file in mongodb how I can generate url for my application

I am not able to solve this problem. there are many ways
I need url like https//local host..
My mongo db my video is store how i can get
you have to save that file in server directory and get the directory path and save in mongodb with your host name and port
Well, We don't actually save videos in MongoDB database. Instead we save them in a folder and save the path to the video in the database.
I'm using NodeJS with MongoDB to achieve the functionality that you want.
Step 1
First create a node project and install these dependencies/libraries in the project.
npm install express dotenv mongoose express-fileupload
express-fileupload library will allow you to accept files in form-data. It creates a property files:[] in the req object.
Step 2
Once, all the essential libraries are installed you can move forward with the second step. We'll write code in the index.js / app.js file.
import express from "express";
import dotenv from 'dotenv';
import upload from 'express-fileupload';
import mongoose from "mongoose";
dotenv.config();
var PORT = process.env.PORT,
DB_URL = process.env.DB_URL;
console.clear();
mongoose.connect(DB_URL, (err) => {
if(err) console.error(err);
console.log('\x1b[33mDatabase Connected!\x1b[0m');
});
const app = express();
app.use(express.json());
app.use(upload());
app.use(cors());
app.use(express.static('public'));
app.listen(PORT, () => {
console.log(`\x1b[92mServer is now up and running on:\x1b[0m`);
console.log(`\x1b[46mhttp://localhost:${PORT}\x1b[0m`);
});
.env
PORT=3000
DB_URL="mongodb+srv://<user>:<password>#:<server.uri>/<db.name>?retryWrites=true&w=majority"
Step 3
Now first of all create a folder public in the top level directory of your project and create a sub folder profiles in it.
All of your images will be saved here. Once, You've performed these instructions you can finally create a route in your main file.
app.post('/saveVideo', async (req, res) => {
try {
let videoFile = req.files?.video;
if (!videoFile) return res.status(400).json({
status: "error",
message: "Please add profile picture to continue.",
data: null,
});
// add file extensions check to identify if the file is a video
// right now, It accepts all kinds of files
let fileName = `public/profiles/${Date.now()}-${videoFile.name.replace(/ /g, '-').toLowerCase()}`;
await profilePicture.mv(fileName);
videoFile = fileName.replace("public", "");
// you can save this videoFile path in the database
return res.json({
status: "success",
message: "Profile Picture Updated!",
data: profilePicture
});
} catch (err) {
return res.status(500).json({
status: "error",
message: "An unexpected error occured while proceeding your request.",
data: null,
trace: err.message
})
})

I want to preserve backend data on reload

I have created an app that has a cart that sends data to the backend, now when I restore I want to preserve the data and display the same data as before.
I have used the PUT method instead of POST and when I send and get data from Firebase, data is preserved on reload and the data from the database is visible, but if I use my own backend in Node.js, I am not able to get the data on reload. This is where I am fetching data from the backend.
export const fetchCartData=()=>{
return async(dispatch)=>{
const getData= async() =>{
const response = await fetch('https://localhost:5000/');
if(!response.ok){
throw new Error('Something went wrong');
}
const info=await response.json();
return info;
};
try{
const data= await getData();
console.log("Here is data from Firebase", data);
dispatch(order_action.replaceCart({
items:data.items || [],
totalQuantity:data.totalQuantity || 0
}));
}catch(error){
dispatch(show_action.showNotification({
status:"failed",
title:"Error...",
message:"Some error occurred."
}));
}
}
}
Tha backend Code is:
const express=require("express");
const bodyParser=require('body-parser');
const cors=require('cors');
const app=express();
app.use(cors());
app.use(bodyParser.json());
app.put("/",function (req,res) {
const data={
items:req.body.items,
totalQuantity:req.body.totalQuantity
}
console.log(data);
console.log("END");
res.send({data});
})
app.get("/",function (req,res) {
console.log(req.body);
const data={
items:req.body.items,
totalQuantity:req.body.totalQuantity
}
res.send({data})
})
app.listen(5000,function () {
console.log("Running on 5000");
})
You can use localStorage on the browser i.e at the client-side. Whenever there is any change in the cart do these steps:
Send data to the backend API using the PUT method and store it in DB or cache(based on the website and users you are dealing with).
Once you get the response from API, update your localStorage data.
localStorage.set('cart', JSON.stringify(dataFromAPI));
Now, on every reload you will always be getting the last updated cart data.
when I send and get data from Firebase, data is preserved on reload
and the data from the database is visible
Just for your knowledge, firebase is a database and when you save data, it is persistent. Now, on reload you must be calling the firebase DB to get the data back that's why you see the data on the client otherwise it is impossible to get data without caching or saving it locally.
You can store/contain the data in a JSON file and reuse the data.
If the data is a stream of data, then you do only need some latest records; you can perform some JavaScript array operations to perform a First-In-First-Out operations by containing up to like 50 or 100 objects/records in the JSON file, so you can later retrieve/reuse.
const fs = require("fs");
const path = require("path");
const data = fs.readFileSync(path.resolve(__dirname,"filename.json");
const data = JSON.parse(data);
data.contentArray.push(req.body); //or any other data
const data = JSON.stringify(data);
fs.writeFileSync(path.resolve(__dirname,"filename.json",data,"utf-8");
/*
filename.json sample
{
"contentArray":[
{
"key":"value"
},
{
"key":"value"
}
]
}
*/
You can find ways to literally store/contain the data in a '.json' file or '.csv' file. I would recommend storing the data in a JSON file, which is way easier.

Upload images into a file server and store the url to the image using nodejs

I am implementing a web app using MEAN Stack and Angular 6. There I want to submit a form with file upload. '.png' files should be uploaded.
I want to save the file in a different file server and send the url to the image.Currently I upload files into a folder in my project and save the image in db (I used ng2fileupload and multer for that.). Then it saves like this.
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAV4AAAFUCAYAAABssFR8AAAK..."
But I want to save the image url and the image should be retrived by the url. Does anyone can explain a proper method for that?
I faced the same problem a month ago and find out a solution to this problem. Though I haven't used multer in the app.
From my frontend, I will be sending an object to Node API endpoint /event which will look like:-
let img = {
content: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
filename: 'yourfile.png'
}
At the backend, I'm using Cloudinary to store my images (Its free plan allows 10GB storage) and returns secure https URLs. So install it using npm i cloudinary and require in your api.js file.
And add the below configuration
cloudinary.config({
cloud_name: 'yourapp',
api_key: 'YOUR_KEY',
api_secret: 'YOUR_SECRET_KEY'
});
Last Step:- (Not so optimized code)
Let say I have an event Schema which has images array, where I'll be storing the URLs returned by cloudinary.
app.post('/event', (req, res) => {
try {
if (req.body.images.length > 0) {
// Creating new Event instance
const event = new Event({
images: [],
});
// Looping over every image coming in the request object from frontend
req.body.images.forEach((img) => {
const base64Data = img.content.split(',')[1];
// Writing the images in upload folder for time being
fs.writeFileSync(`./uploads/${img.filename}`, base64Data, 'base64', (err) => {
if (err) {
throw err;
}
});
/* Now that image is saved in upload folder, Cloudnary picks
the image from upload folder and store it at their cloud space.*/
cloudinary.uploader.upload(`./uploads/${img.filename}`, async (result) => {
// Cloudnary returns id & URL of the image which is pushed into the event.images array.
event.images.push({
id: result.public_id,
url: result.secure_url
});
// Once image is pushed into the array, I'm removing it from my server's upload folder using unlinkSync function
fs.unlinkSync(`./uploads/${img.filename}`);
// When all the images are uploaded then I'm sending back the response
if (req.body.images.length === event.images.length) {
await event.save();
res.send({
event,
msg: 'Event created successfully'
});
}
});
});
}
} catch (e) {
res.status(400).send(e);
}
});
P.S. Go ahead and suggest some optimization solution for this code here

Upload and Retrieve PDF file to PostgreSQL using Node.js, Express and Knex

I am trying to upload and retrieve a PDF file to and from a PostgreSQL Db using Node.js, Express and Knex.
The DB column documentfile is file type Bytea.
The file seems to be uploading to the DB and is returned. The resulting PDF file (test3.pdf) does not want to open. I have been struggling with this for a long time now. Any help will be greatly appreciated!
Node.js code to upload the file :
app.get('/uploadfile', (req,res) =>{
fs.readFile('0203.pdf' function(err, imgData) {
console.log(imgData)
knex('documenttable').insert(
{documentname: '0203.pdf',
documentfile: [imgData]
})
.then( function (result) {
res.end(console.log("Document uploaded To DB"));
});
});
});
Node.js code to retrieve the file:
app.get('/dbfileback', function(req, res, next) {
knex('documenttable').where({
documentnumber: '1'
}).select('documentname', 'documentfile')
.then( function (result) {
fs.writeFile('test3.pdf', result[0].documentfile,'binary');
res.end(console.log("Done"));
});
});

Node.js to send images via REST API

Im struggling to find material on this
I have a rest API, written in node.js, that uses mongoDB.
I want users to be able to upload images (profile pictures) and have them saved on the server (in mongoDB).
A few questions, Ive seen it is recommended to use GridFS, is this the best solution?
How do i send these files? Ive seen res.sendFile, but again is this the best solution?
If anyone has any material they can link me I would be appreciative
thanks
You won't be able to get the file object on the server directly. To get file object on the server, use connect-multiparty middleware. This will allow you to access the file on the server.
var multipart = require('connect-multiparty');
var multipartmiddleware = multipart();
var mv = require('mv');
var path = require('path');
app.post("/URL",multipartmiddleware,function(req,res){
var uploadedImage = req.files.file;
for (var i = 0; i < uploadedImage.length; i++) {
var tempPath = uploadedImage[i].path;
var targetPath = path.join(__dirname ,"../../../img/Ads/" + i + uploadedImage[i].name);
mv(tempPath, targetPath, function (err) {
if (err) { throw err; }
});
}
})
Use file system
Generally in any database you store the image location in the data as a string that tells the application where the image is stored on the file system.
Unless your database needs to be portable as a single unit, the storing of images inside of the database as binary objects generally adds unnecessary size and complexity to your database.
-Michael Stearne
In MongoDB, use GridFS for storing files larger than 16 MB.
- Mongo Documentation
Therefore unless your images will be over 16 MB, you should either store the file on a CDN (preferable) or the server's own file system and save its URL to user's document on the database.
Local file system implementation
This method uses Busboy to parse the photo upload.
in relevant html file:
<input type="file" title="Choose a file to upload" accept="image/*" autofocus="1">
Handler function for your photo upload route in server file (you will need to fill in the variables that apply to you and require the necessary modules):
function photoUploadHandlerFunction (req, res) {
var busboy = new Busboy({ headers: req.headers })
busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
const saveToDir = path.join(__dirname, uploadsPath, user.id)
const saveToFile = path.join(saveToDir, filename)
const pathToFile = path.join(uploadsPath, user.id, filename)
const writeStream = fs.createWriteStream(saveToFile)
createDirIfNotExist(saveToDir)
.then(pipeUploadToDisk(file, writeStream))
.then(findUserAndUpdateProfilePic(user, pathToFile))
.catch((err) => {
res.writeHead(500)
res.end(`Server broke its promise ${err}`)
})
})
busboy.on('finish', function () {
res.writeHead(200, { 'Connection': 'close' })
res.end("That's all folks!")
})
return req.pipe(busboy)
}
Where the promise functions createDirIfNotExist and pipeUploadToDisk could look like this:
function createDirIfNotExist (directory, callback) {
return new Promise(function (resolve, reject) {
fs.stat(directory, function (err, stats) {
// Check if error defined and the error code is "not exists"
if (err) {
if (err.code === 'ENOENT') {
fs.mkdir(directory, (err) => {
if (err) reject(err)
resolve('made folder')
})
} else {
// just in case there was a different error:
reject(err)
}
} else {
resolve('folder already existed')
}
})
})
}
function pipeUploadToDisk (file, writeStream) {
return new Promise((resolve, reject) => {
const fileWriteStream = file.pipe(writeStream)
fileWriteStream.on('finish', function () {
resolve('file written to file system')
})
fileWriteStream.on('error', function () {
reject('write to file system failed')
})
})
}
To answer your question 'How do I send these files?', I would need to know where to (MongoDB, to the client...). If you mean to the client, you could serve the static folder where they are saved.
If you still want to learn about implementing GridFs tutorialspoint have a good tutorial
More material
Good tutorial on handling form uploads
Tutorial using the node-formidable module
If you're using the mongoose odm you can use the mongoose-crate module and send the file wherever for storage.
Also, this is a good case for shared object storage like AWS S3 or Azure blob storage. If you are running a distributed setup in something like AWS, you usually don't want to store photos on the local server.
Store the url or key name in the database that points to the S3 object. This also integrates with CloudFront CDN pretty easily.
As suggested before. MultiPart for the actual upload.

Resources