I am trying to upload, save and display images in MongoDB using express server and GridFS.
Data is sent with FormData with vue.js on the front-end and that part works well.
Images are getting to the server, and it stores that data, but I can not display the uploaded image, instead I get this
It displays the Image in some encoding I guess.
Here is server code for receiving, storing and displaying images.
const express = require('express')
const mongodb = require('mongodb')
const path = require('path')
const crypto = require('crypto')
const mongoose = require('mongoose')
const multer = require('multer')
const GridFsStorage = require('multer-gridfs-storage')
const Grid = require('gridfs-stream')
const methodOverride = require('method-override')
const config = require('../../config/keys')
const morgan = require('morgan')
const images = express()
// MIDDLEWARE
images.use(methodOverride('_method'))
// images.use(morgan("default"))
// MONGO URI
const mongoURI = config.dbUrl
// CREATE MONGO CONNECTION
const conn = mongoose.createConnection(mongoURI)
// INITALIZE GRID FS
let gfs
conn.once('open', () => {
gfs = Grid(conn.db, mongoose.mongo)
gfs.collection('uploads')
})
// CREATING STORAGE ENGINE
const storage = new GridFsStorage({
url: mongoURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
const filename = file.originalname
const fileInfo = {
filename,
bucketName: 'uploads'
}
resolve(fileInfo)
})
}
})
const upload = multer({ storage })
// POST REQUEST UPLOADS FILE TO DATABASE
images.post('/', upload.single('image'), (req, res) => {
console.log('POST REQUEST TO HTTP://LOCALHOST:3000/API/BACKEND/IMAGES/')
res.json(req.file)
console.log(req.file)
})
// GET REQUEST
images.get('/', async (req, res) => {
console.log('GET REQUEST TO HTTP://LOCALHOST:3000/API/BACKEND/IMAGES/')
const posts = await load();
res.send(await posts.find({}).toArray())
})
images.get('/:filename', (req, res) => {
gfs.files.findOne({ filename: req.params.filename }, (err, file) => {
// Check if file
if (!file || file.length === 0) {
return res.status(404).json({
err: 'No file exists'
});
}
// Check if image
if (file.contentType === 'image/jpeg' || file.contentType === 'image/png') {
// Read output to browser
const readstream = gfs.createReadStream(file.filename);
readstream.pipe(res);
} else {
res.status(404).json({
err: 'Not an image'
});
}
});
});
// LOADING FUNCTION FOR MONGODB
async function load() {
const client = await mongodb.MongoClient.connect
(`${config.dbUrl}`, {
useNewUrlParser: true,
useUnifiedTopology: true
});
return client.db('myCluster').collection('uploads.files');
}
module.exports = images
Every help would be nice and much appreciated! Thanks! :)
Related
I want to resize and compress images using sharp library in node.js. The code was taken from this article, but for some reason returns an error.
Error
PS D:\Code\Web\image-compressor-nodejs> node index.js
[Error: ./uploads/2022-08-10T18:35:49.251Z-2021-11-18_23.44.45.png.webp: unable to open for write
windows error: ���������� �� �������� �������.
]
Code
const express = require("express");
const multer = require("multer");
const sharp = require("sharp");
const fs = require("fs");
const app = express();
const storage = multer.memoryStorage();
const upload = multer({ storage });
app.use(express.static("./uploads"));
app.post("/", upload.single("picture"), async (req, res) => {
try {
fs.access("./uploads", (error) => {
if (error) {
fs.mkdirSync("./uploads");
}
});
const { buffer, originalname } = req.file;
const timestamp = new Date().toISOString();
const ref = `${timestamp}-${originalname}.webp`;
await sharp(buffer)
.webp({ quality: 20 })
.toFile("./uploads/" + ref);
const link = `http://localhost:3000/${ref}`;
return res.json({ link });
} catch (error) {
console.log(error);
}
});
app.listen(3000);
I am new in ExpressJs and working on creating api for one of a dashboard created in reactjs. There is a form in a dashboard which is collecting some of information from the users like "title", "description" and "image". I have created an express server to collect that information and to save it into mongodb. For images What I have done is that, I am uploading image to Cloudinary and storing uploaded url and public_id into database.
So after following some of tutorials I have done something like this.
index.js
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const db = require("./db");
// Api router import goes here
const sectionTypesRouter = require("./routes/section-types-router");
const app = express();
const apiPort = 3000;
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
app.use(bodyParser.json());
db.on("error", console.error.bind(console, "MongoDB connection error:"));
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.use("/api", sectionTypesRouter);
app.listen(apiPort, () => console.log(`Server running on port ${apiPort}`));
Than, First I have create a file multer.js :
const multer = require("multer");
const storage = multer.diskStorage({
destination: "public/uploads",
filename: (req, file, cb) => {
cb(null, file.fieldname + "-" + Date.now());
},
});
const fileFilter = (req, file, cb) => {
if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
cb(null, true);
} else {
//reject file
cb({ message: "Unsupported file format" }, false);
}
};
const upload = multer({
storage: storage,
fileFilter: fileFilter,
});
module.exports = upload;
Below is my api router section-type-router.js :
const express = require("express");
const upload = require("../utils/multer");
const SectionTypesCtrl = require("../controllers/section-types-ctrl");
const router = express.Router();
router.post(
"/section-type",
upload.single("image"),
SectionTypesCtrl.createSectionType
);
router.get("/section-types", SectionTypesCtrl.getSectionTypes);
module.exports = router;
This is the section-type-ctrl.js :
const SectionType = require("../models/section-type-model");
const fs = require("fs");
const path = require("path");
const cloudinaryUploader = require("../utils/cloudinaryUploader");
const createSectionType = async (req, res) => {
const body = req.body;
if (!body) {
return res.status(400).json({
success: false,
error: "Required parameter are missing",
});
}
cloudinaryUploader
.uploads(req.file.path, "Section-Types")
.then((result) => {
const sectionType = new SectionType({
title: body.title,
description: body.description,
image: {
url: result.url,
publicId: result.public_id,
},
});
sectionType
.save()
.then(() => {
return res.status(201).json({
success: true,
id: sectionType._id,
message: "Section type created!",
});
})
.catch((error) => {
return res.status(400).json({
error,
message: "Section type not created!",
});
});
})
.catch((error) => {
res.status(500).send({
message: "failure",
error,
});
});
};
module.exports = {
createSectionType,
};
And lastly this is cloudinaryUpload.js :
const cloudinary = require("../config/cloudinary");
exports.uploads = (file, folder) => {
return new Promise((resolve) => {
cloudinary.uploader.upload(
file,
{
resource_type: "auto",
folder: folder,
},
(err, result) => {
if (!err) {
resolve({
url: result.url,
public_id: result.public_id,
});
} else {
throw err;
}
}
);
}).catch((error) => {
throw error;
});
};
Now, everything is working properly. Images is uploading to the cloudinary and returned url and public_id is storing in database. But the problem is that image that I have uploaded is also upload on local directory public/uploads/. This will may create a storage issue while host a site. So Is there any best way to upload image directly to the cloudinary without creating a copy in local directory which also should work on production mode ?
In your example, the file is being stored to public/uploads on your server because you're telling multer to do so via multer.diskStorage
As #Molda's comment above says, you can avoid this by using the multer-storage-cloudinary package to have Multer store the file in Cloudinary automatically.
Another possibility is to change how you're using Multer so it doesn't store the file anywhere, then take the uploaded file while it's in memory and pass it to Cloudinary's SDK as a stream.
There's an example of this in this blog post on the Cloudinary site: https://cloudinary.com/blog/node_js_file_upload_to_a_local_server_or_to_the_cloud
In your case, you can stop using multer.diskStorage, in favour of just using multer() then use streamifier or another library to turn the uploaded file into a stream, and pass that to cloudinary.uploader.upload_stream()
Instead of storing file directly into mongodb I want to store using mongoose library. How to use mongoose library using the code frame same?
Update:: Saved in mongoose library. Now it uploads all of the file but how can I just store only the docx file instead of image file. And uploading anyother file should say invalid file.
update 2:: Resolved with the issue. Now capable of only allowing docxx file. Now need to add filename with extention as mentioned in the comment section. How to use that part of code.
Middleware::upload.js
const util = require("util");
const multer = require("multer");
const GridFsStorage = require("multer-gridfs-storage");
const mongoose = require('mongoose');
const Grid = require('gridfs-stream');
const mongoURI ='mongodb://localhost:27017/file_uploaded';
const promise = mongoose.connect(mongoURI, { useNewUrlParser: true });
const conn = mongoose.connection;
let gfs;
conn.once('open',() => {
gfs = Grid(conn, mongoose.mongo);
gfs.collection('uploads');
});
var storage = new GridFsStorage({
db: promise,
options: { useNewUrlParser: true, useUnifiedTopology: true },
file: (req, file) => {
return new Promise((resolve, reject) => {
if (file.mimetype === 'application/vnd.openxmlformats-
officedocument.wordprocessingml.document') {
resolve({
bucketName: 'words'
})
***//Add filename inorder to maintain original final name with
extention***
//const filename = `${Date.now()}-
bezkoder-${file.originalname}`;
//return filename;
} else {
reject(Error("File type has been rejected"));
}
});
}
});
var uploadFile = multer({ storage: storage }).single("file");
var uploadFilesMiddleware = util.promisify(uploadFile);
module.exports = uploadFilesMiddleware;
You dont need to use GridFsStorage. Try below code.
// file-controller.js
const express = require('express');
const mongoose = require('mongoose');
const multer = require('multer');
const router = express.Router();
const multerStorage = multer.memoryStorage();
const upload = multer({ storage: multerStorage, });
const imageSchema = mongoose.Schema({
image: { data: Buffer, contentType: String },
}, { timestamps: true });
const ImageModel = mongoose.model('images', imageSchema);
router.post('/upload', upload.single('image'), async (req, res, next) => {
const image = { data: new Buffer.from(req.file.buffer, 'base64'), contentType: req.file.mimetype }
const savedImage = await ImageModel.create(image);
res.send(savedImage);
});
router.get('/getImage/:id', async (req, res, next) => {
const { id: _id } = req.params;
// If you dont use lean(), you wont decode image as base64
const image = await ImageModel.findOne({ _id }).lean().exec();
res.send(image);
});
module.exports = router;
Serve route via express.
const express = require('express');
const FileController = require('./file-controller');
const app = express();
// Mongo DB connection code ...
// Serve static files code ...
app.use('/file', FileController);
app.listen(3000, () => console.log(`Example app listening on port ${port}!`));
I have a problem uploading image file to my mongoDB using gridFS, but for some reason this doesn't work
this is my code :
const config = require("config");
const express = require("express");
const router = express.Router();
const dbURI = config.get("mongoURI");
const multer = require("multer");
const crypto = require("crypto");
const path = require("path");
const GridFsStorage = require("multer-gridfs-storage");
var storageImage = new GridFsStorage({
url: dbURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString("hex") + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: "user_images"
};
resolve(fileInfo);
});
});
}
});
const uploadImage = multer({ storageImage });
router.post("/uploadImage", uploadImage.single("userImg"), (req, res) => {
console.log("uploading");
console.log(req.file);
res.json({ msg: "file uploaded successfully" });
});
module.exports = router;
when I console .log req.file I get undefined, does anyone know what the problem is?
I found the problem:
const uploadImage = multer({ storageImage });
it should be
const uploadImage= multer({storage:storageImage});
I'm trying to make a file upload API using multer gridFS, I used the documentation but I'm missing something:
this is my code:
const config = require("config");
const express = require("express");
const router = express.Router();
const dbURI = config.get("mongoURI");
const multer = require("multer");
const crypto = require("crypto");
const path = require("path");
const GridFsStorage = require("multer-gridfs-storage");
var storage = new GridFsStorage({
url: dbURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString("hex") + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: "uploads"
};
resolve(fileInfo);
});
});
}
});
const upload = multer({ storage });
router.post("/upload", upload.single("file"), (req, res) => {
res.json({ msg: "file uploaded successfully" });
});
I get an error TypeError: Router.use() requires a middleware function but got a Object which basically tells me that upload.single is not a function, what is my error?
I found the mistake, I was missing module.exports = router;