I am learning node.js at the moment and I am creating a little application where I can upload images and display them in a gallery.
Currently I have a form which uploads the image to the server via POST
extends ../layout
block content
.col-sm-6.col-sm-offset-3
h1 control panel
form(action="/upload", method="POST",
enctype="multipart/form- data")
input(type="file", name='image')
input(type="submit", value="Upload Image")
The file is then inserted to a mongodb using mongoose
exports.upload = function (req, res) {
fs.readFile(req.files.image.path, function (err, data) {
var imageName = req.files.image.name;
if(!imageName){
console.log("seems to be an
error this file has not got a name");
res.redirect("/");
res.end();
}else{
var newimage = new Image();
newimage.img.data = fs.readFileSync(req.files.image.path);
newimage.img.name = imageName;
newimage.save(function (err, a) {
if (err){
console.log("There was an error saving the image")
res.redirect("/");
res.end();
}
res.redirect("/gallery");
});
}
});
}
In my gallery controller I query the database for all the images and pass them to front-end.
exports.gallery = function (req, res) {
Image.find({}, function(err, image){
if (err)
res.send(err);
else
res.render("site/gallery", {images: image });
});
}
And then in my gallery I try create a new image tag for each of the images
extends ../layout
block content
h1 Gallery
each image in images
img(src='#{image.img.data}')
My problem is that I keep getting a 404 error because the browser cannot find the image.
But I have a feeling that I might be going about this the wrong way. I have seen GridFS but I feel that it is not suitable for this app as the amount of images in the gallery will be less than 20 max. Am I going about the right way to do this or should I be storing the images on the server and retrieving them that way?
You would typically upload the images to your server's machine filesystem or to a static assets cloud hosting service like AWS S3, and store only the URLs of the images in your database.
You could also use a solution like Cloudinary.
Related
I've created a SNS web application (using node.js) where people can upload pictures (using cloudinary) into collections.
The application works perfectly on a cloud-based IDE I use for writing code; however, after pushing it to heroku, the image upload no longer works. I don't get any error message in my console, even though I think it should be console.logging it, however the error flash happens and the image doesn't upload.
Here is my code:
router.post("/", middleware.isLoggedIn, upload.single('image'), function(req, res){
cloudinary.v2.uploader.upload(req.file.path, function(err, result) {
if (err) {
//if error
console.log(err.message);
req.flash("error", "Can't upload image, try again later.");
return res.redirect("back");
}
//else
// add cloudinary url for the image to the campground object under image property
req.body.image = result.secure_url;
req.body.imageId = result.public_id;
// add author to campground
req.body.author = {
id: req.user._id,
username: req.user.username
};
I've tried searching other posts for a possible reason; but I can't find anything that matches my situation. If anyone can help...please, your advice would be greatly appreciated!
Thank you.
Sorry! The problem was the CLOUD_NAME config variable had '' marks around it.
If anyone is having similar issues, try removing the quotes around the name variable.
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
hope someone can help me with this:
I am working on a Node/Express/Mongo CRUD app where every post/document has ONE image. I am using multer and cloudinary for uploading the images. However, I want users to be able to upload multiple images to each post. Ideally, the image urls/paths and IDs would be stored in arrays in my database.
I have been trying and researching for hours now but cannot seem to make it work, and there is no tutorial out there explaining how this (simple) task can be achieved using multer and cloudinary.
This is the code I am using to upload ONE image per post, which is working:
// CREATE Route - add new post to DB
router.post("/", middleware.isLoggedIn, upload.single('image'), function(req, res) {
cloudinary.v2.uploader.upload(req.file.path, function(err, result) {
if(err) {
req.flash('error', err.message);
return res.redirect('back');
};
// add cloudinary url for the image to the post object under image property
req.body.post.image = result.secure_url;
// add image's public_id to post object
req.body.post.imageId = result.public_id;
// add author to post
req.body.post.author = {
id: req.user._id,
username: req.user.username
}
Post.create(req.body.post, function(err, post) {
if (err) {
req.flash('error', err.message);
return res.redirect('back');
}
res.redirect('/posts/' + post.id);
});
});
});
How would I need to change this code in order to achieve this goal?
Thanks in advance for your help!
The upload API currently only supports a single file upload at a time, it does have a high concurrency rate (of about 40-50 concurrent calls) so you can use multi-threads to upload many files at once.
You can also use asynchronous calls, and tell Cloudinary to do the upload in the background by adding the async parameter and setting it to true.
How do I create a web gallery and uploading, storing and displaying images via nodejs and mongodb?
I have tried to write some code myself, but don't manage to solve the problem.
Do someone have a link or tutorial that helps me solve the problem?
It is not recommended to store whole images on databases. It can be done but based on similar questions on stack overflow it is better to store images on your file system. That can be done by using themulterand fs module to handle the upload and store it on the file system. You can even use an image proccessor to confirm that what was uploaded was really an image and not something else. I recommend using the sharp module found on npm to do that. This way you are sure that nothing can go wrong and you can even resize images before storing. Here is some code for this using express.js:
var multer = require('multer');
var uploadPicture = multer({
dest: 'temp/'
});
var sharp = require('sharp');
app.post('/upload', uploadPicture.single('profileIcon'), function (req,res) {
fs.readFile(req.file.path, function (err, data) {
if (err) res.end('UNRESOLVABLE ERROR');
sharp(data).resize(200, 200).toFile('./photos/pic.jpg', function (err, info) {
//DELETE THE TEMPORAL FILE
fs.unlink(req.file.path, function (error) {
if (error) res.end('UNRESOLVABLE ERROR'); //CODE 3 ALARM
res.end('success');
});
}
So, bit of an odd problem. I have a bunch of media files saved as base64 strings in mongo, some are images, some are videos.
I made an API for getting the media files:
app.get('/api/media/:media_id', function (req, res) {
media.findById(req.params.media_id)
.exec(function (err, media) {
if (err) {
res.send(err);
}
var file = new Buffer(media.file, 'base64');
res.writeHead(200, {'Content-Type': media.type, 'Content-Transfer-Encoding': 'BASE64', 'Content-Length': file.length});
res.end(file);
});
});
Now, images have no problems. They load just fine, both directly from the API, and when I call the API from a front-end (for example <img src="/api/media/23498423">)
THE PROBLEM
If I fetch a video from a front-end, like the images - but with a video- or object-tag:
<video src="/api/media/3424525" controls></video>
there's no problem, but if I load the video in a browser directly from the API:
http://localhost:8080/api/media/3424525
the server process crashes, no errors. It simply just freezes up. And we're not talking about huge video files - it's a 1.5MB video.
The media type in the header for all the videos I'm testing with is video/mp4. Oh, and just to be clear: if I do the same with images, everything works perfectly.
EDIT:
Okay, so as suggested by #idbehold and #zeeshan I took a look at gridfs and gridfs-stream, and for the purpose of my app, this certainly is what I should have used in the first place. However, after implementing gridfs in my app, the problem still persists.
app.get('/api/media/:media_id', function (req, res) {
gfs.findOne({ _id: req.params.media_id }, function (err, file) {
if (err) {
return res.status(400).send(err);
}
if (!file) {
return res.status(404).send('');
}
res.set('Content-Type', file.contentType);
res.set('Content-Disposition', 'inline; filename="' + file.filename + '"');
var readstream = gfs.createReadStream({
_id: file._id
});
readstream.on("error", function (err) {
console.log("Got an error while processing stream: ", err.message);
res.end();
});
readstream.pipe(res);
});
});
When I call the media file (be it image or video) from a front-end, within a HTML tag, everything works out fine. But if I load a video (again, smallish videos from 1.5mb to max 6mb total size) directly in the browser, the server process freezes. To be a bit more clear: I am testing on windows, and the server app (server.js) is run in console. The console and the process it is running is what freezes. I cannot load any more pages/views in the node app, and I cannot even stop/kill/shutdown the node app or the console.
Streaming videos directly to/from GridFS using gridfs-stream either with mongodb-native db instance or mongoose.
var mongo = require('mongodb'),
Grid = require('gridfs-stream'),
db = new mongo.Db('yourDatabaseName', new mongo.Server("127.0.0.1", 27017)),
gfs = Grid(db, mongo);
//store
app.post('/video', function (req, res) {
req.pipe(gfs.createWriteStream({
filename: 'file_name_here'
}));
res.send("Success!");
});
//get
app.get('/video/:vid', function (req, res) {
gfs.createReadStream({
_id: req.params.vid // or provide filename: 'file_name_here'
}).pipe(res);
});
for complete files and running project:
Clone node-cheat direct_upload_gridfs, run node app followed by npm install express mongodb gridfs-stream.
Truly an odd problem...
I could be way off, but it's worth a shot:
One of the differences when opening a url directly from the browser is that the browser will also try to fetch http://localhost:8080/favicon.ico (while trying to find the tab icon). Maybe the problem is not related to your video code, but rather to some other route, trying to handle the /favicon.ico request?
Have you tried using wget or curl?
I don't know the answer, maybe this is a dumb suggestion, but what is the browser you are using? Maybe something from Microsoft causes the problem...