Adding link to Mongo DB when uploading image to AWS S3 Bucket - node.js

In my Express backend, I have set up a connection with S3 Bucket for uploading images, and it works.
However additionally, I would like to be able to store a reference link (S3 url) of the saved image in my Mongo Database.
I have been trying to play around with req.file object but somehow, I cannot get the req.file.location, whereas req.file.buffer works okay (as in the example below in itemController.js). Is there any problem in my s3.js configuration? Or pehraps I would need a different approach to get req.file.location instead of buffer?
Below my bucket configuration s3.js
// s3.js
const AWS = require('aws-sdk')
// s3 bucket configuration
const awsConfig = {
accessKeyid : process.env.S3_ACCESS_KEY,
secretAccessKey : process.env.S3_ACCESS_SECRET,
region : process.env.S3_REGION
}
const S3 = new AWS.S3(awsConfig)
//s3 bucket upload function
const uploadToS3 = (fileData) => {
return new Promise ((resolve, reject) =>{
const params = {
Bucket : process.env.S3_BUCKET_NAME,
Key: `${Date.now().toString()}.jpg`,
Body: fileData
}
S3.upload(params, (err, data) =>{
if(err){
console.log(err)
reject(err)
}
console.log(data)
return resolve(data)
})
})
}
module.exports = {
uploadToS3
}
Here is my itemController.js
const Item = require('../models/itemModel')
const Worker = require('../models/workerModel')
const mongoose = require('mongoose')
const multer = require('multer')
const { uploadToS3 } = require('../s3')
//! Multer configuration
const multerConfig = {
limits: 1024 * 1024 * 5,
fileFilter: function (req, file, done) {
if (file.mimetype === "image/jpg"|| file.mimetype === "image/png" || file.mimetype ==='image/jpeg') {
done(null, true)
} else {
done("Niewłaściwy plik, użyj .jpg .jpeg .png", false)
}
}
}
const upload = multer(multerConfig)
//! CREATE new item
const createItem = async (req, res) => {
// multer middleware that handles file upload
upload.single("image")(req, res, async () => {
//destructuring form req.body
const {
title,
model,
producer,
serialNumber,
yearOfProduction,
atEmployee,
seller,
warrantyDate,
purchaseDate,
image,
} = req.body
if (!title){
return res.status(400).json({error:'Błąd! Wymagane jest podanie chociaż nazwy narzędzia.'})
}
//try-catch to create new Item and catch error. Add "await" because of "async" - Js promise above
try {
const item = await Item.create({
title,
model,
producer,
serialNumber,
yearOfProduction,
atEmployee,
seller,
warrantyDate,
purchaseDate,
image: req.file ? req.file.buffer : image,
})
if (req.file) {
// upload file to S3 and store the URL in the database
const result = await uploadToS3(req.file.buffer)
item.imageUrl = result.location
await item.save()
}
res.status(200).json(item)
} catch(error) {
res.status(400).json({error: error.message})
}
})
}
...
And here is my ItemModel.js
const mongoose = require('mongoose')
//mongoose function to create new model Schema
const Schema = mongoose.Schema
const itemSchema = new Schema ({
title: {
type: String,
required: true,
},
producer: {
type: String,
required: false,
},
model: {
type: String,
required: false,
},
serialNumber: {
type: String,
required: false,
},
yearOfProduction:{
type: Number,
required: false
},
seller:{
type: String,
required: false
},
purchaseDate: {
type: Date,
default: Date.now
},
warrantyDate: {
type: Date,
required: false,
},
//Linking Worker model to an Item
atEmployee: {
type: mongoose.Schema.Types.ObjectId,
required: false,
ref:'Worker',
},
image: {
type: String,
required: false,
}
}, { timestamps: true })
module.exports = mongoose.model('Item', itemSchema)

This is how I actually solved it
itemController.js
const Item = require('../models/itemModel')
const Worker = require('../models/workerModel')
const mongoose = require('mongoose')
const multer = require('multer')
const { uploadToS3 } = require('../s3')
//! Multer configuration
const multerConfig = {
limits: 1024 * 1024 * 5,
fileFilter: function (req, file, done) {
if (file.mimetype === "image/jpg"|| file.mimetype === "image/png" || file.mimetype ==='image/jpeg') {
done(null, true)
} else {
done("Niewłaściwy plik, użyj .jpg .jpeg .png", false)
}
}
}
const upload = multer(multerConfig)
//! CREATE new item
const createItem = async (req, res) => {
// multer middleware that handles file upload
upload.single("image")(req, res, async () => {
//destructuring form req.body
const {
title,
model,
producer,
serialNumber,
yearOfProduction,
atEmployee,
seller,
warrantyDate,
purchaseDate,
image,
} = req.body
if (!title){
return res.status(400).json({error:'Błąd! Wymagane jest podanie chociaż nazwy narzędzia.'})
}
//try-catch to create new Item and catch error. Add "await" because of "async" - Js promise above
try {
let item = {}
if (req.file) {
// upload file to S3 and store the URL in the database if image has been uploaded
const result = await uploadToS3(req.file.buffer)
item = await Item.create({
title,
model,
producer,
serialNumber,
yearOfProduction,
atEmployee,
seller,
warrantyDate,
purchaseDate,
image: result.Location,
})
//if no image, show nothing
} else {
item = await Item.create({
title,
model,
producer,
serialNumber,
yearOfProduction,
atEmployee,
seller,
warrantyDate,
purchaseDate,
})
}
res.status(200).json(item)
} catch(error) {
res.status(400).json({error: error.message})
}
})
}

Related

Getting a not found error when trying to retrieve my audio files from mongoDB

When trying to retrieve my audio file from the backend server and then display it on the front I keep getting an error stating that GET http://localhost:4004/audio/song.m4a 404 (Not Found). I am using GridFS and multer to store the audio files inside of mongoDB. I was able to successfully send my audio file inside of the database but unable to retrieve the audio file and display it on the front end.
here is my front end code
import React, { useState, useEffect } from 'react';
function AudioUpload() {
const [audioFiles, setAudioFiles] = useState([]);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch('http://localhost:4004/audio');
const data = await response.json();
console.log(data);
if (data.length > 0) {
setAudioFiles(data);
}
} catch (error) {
console.error(error);
}
}
fetchData();
}, []);
return (
<div>
{audioFiles.map((audio, index) => (
<div key={index}>
<audio src={`http://localhost:4004/audio/${audio.filename}`} controls>
Your browser does not support the audio element.
</audio>
</div>
))}
</div>
);
}
export default AudioUpload;
and here is my backend code:
const express = require('express');
const mongoose = require('mongoose');
const multer = require('multer');
const Grid = require('gridfs-stream');
const { GridFsStorage } = require('multer-gridfs-storage');
const cors = require("cors");
const app = express();
app.use(cors());
const conn = mongoose.createConnection('mongodb+srv://jordandeeds31:Jd400089#cluster0.iwva5ci.mongodb.net/grid2?retryWrites=true&w=majority', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
let gfs;
conn.once('open', () => {
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('audioFiles');
console.log("connected to db");
});
const storage = new GridFsStorage({
url: 'mongodb+srv://jordandeeds31:Jd400089#cluster0.iwva5ci.mongodb.net/grid2?retryWrites=true&w=majority',
file: (req, file) => {
return {
filename: file.originalname,
bucketName: 'audioFiles',
};
},
});
const upload = multer({ storage: storage });
const audioSchema = new mongoose.Schema({
filename: { type: String, required: true },
contentType: { type: String, required: true },
fileId: { type: mongoose.Schema.Types.ObjectId, required: true },
});
const Audio = conn.model('Audio', audioSchema);
app.post('/upload', upload.single('audio'), async (req, res) => {
try {
const { originalname, mimetype } = req.file;
const fileId = req.file.id;
const audio = new Audio({
filename: originalname,
contentType: mimetype,
fileId,
});
await audio.save();
res.status(200).json({ message: 'File uploaded successfully' });
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Error uploading file' });
}
});
app.get('/audio', async (req, res) => {
try {
const audioList = await Audio.find();
console.log(audioList)
if (!audioList) {
return res.status(404).json({ message: 'No audio files found' });
}
res.json(audioList);
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Error retrieving files' });
}
});
app.listen(4004, () => {
console.log('Server listening on port 4004');
});

Getting 400 Error when uploading image as formdata from flutter app (with dio) using Nodejs as backend

I created a Nodejs backend with MongoDB as a database, in the frontend, I am using flutter. I want to store image in MongoDB, so I am sending post request from the flutter app using dio package. For authentication I am using middleware in the backend to authenticate web tokens, which is working fine. But after sending a post request and authenticating returning the right user, it is giving 400 error.
authentication middleware
const auth = async function(req,res,next){
try {
const token = (req.headers.authorization).split(' ')[1]
const decoded_token = jwt.verify(token,'ankan12345')
const user = await User.findOne({_id:decoded_token._id,"tokens.token":token})
//console.log(user)
if (!user){
throw new Error()
}
req.user = user
req.token = token
next()
} catch(e){
res.status(400).send("Please Authenticate")
}
}
User model
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
unique: true,
validate(val) {
if (!validator.isEmail(val)) {
throw new Error('Email invalid')
}
}
},
password: {
type: String
},
dp:{
type: Buffer
},
tokens: [{
token: {
type: String,
required: true,
}
}]
}, {
timestamps: true
})
Node js image upload endpoint
const upload = multer({
// For local storage uncomment this
// dest:'./challenge_39/images',
limits: {
fileSize: 1000000000
},
fileFilter(req,file,cb) {
if(!file.originalname.match(/\.(jpg|PNG|png|jpeg)$/)){
return cb(new Error("File type wrong"))
}
cb(undefined,true)
}
})
// Auth and storing data binary to db
router.post('/upload',auth,upload.single('file_upload'),async (req,res)=>{
//const modified = await sharp(req.file.buffer).resize({width:300,height:300}).toBuffer()
// without modification
req.user.dp = req.file.buffer
//req.user.dp = modified
req.user.save()
res.send()
},(error,req,res,next)=>{
console.log("fdghf")
res.status(400).send("error Upload a document")
})
flutter dio request
Future<dynamic> UploadDp(XFile img ) async {
String filename = img.path.split('/').last;
print(img.path);
print(filename);
try{
dio.options.headers["Authorization"] = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWJmMmMwNjg5MTVlOTdiNWMzZDAyNTAiLCJpYXQiOjE2Mzk5MTg3MzB9.4WCFxWtLc_wioexs0rq24bGKRG36oR0bzbMOgEjdRiA";
FormData formData = new FormData.fromMap({
"dp": await MultipartFile.fromFile(
img.path,
filename: filename,
//contentType: MediaType("image", "jpg")
//contentType: MediaType
)
});
var response = await dio.post(
'http://192.168.8.116:3000/upload',
data: formData
);
return response;
} catch(e){
return e;
}
}
Flutter button function
ElevatedButton(
onPressed: () async {
XFile image;
var image_picker = await _picker.pickImage(source: ImageSource.camera);
if(image_picker!=null){
setState(() {
image = image_picker;
});
}
apirepo.UploadDp(image).then((data)=>{
print(data)
});
},
child: Text(
"Upload DP"
)
)

File upload using Express.js (multer) is not working

I'm trying to implement file upload using multer in express.js but file is not uploading in db can any one help me in this .
Here is my code:
csr.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const schema = new Schema({
commonName: { type: String },
orgName: { type: String },
orgUnit: { type: String },
city: { type: String },
state: { type: String },
country: { type: String },
emailAdd: { type: String },
file: { type: String },
}, { collection: 'csr' });
schema.set('toJSON', {
virtuals: true,
versionKey: false,
transform: function (doc, ret) {
delete ret._id;
delete ret.hash;
}
});
module.exports = mongoose.model('Csr', schema);
csr.controller.js
const express = require("express");
const router = express.Router();
const userService = require("./csr.service");
router.post("/fileupload",fileupload)
module.exports = router;
function fileupload(req, res, next) {
userService
.fileupload()
.then(() =>{
//console.log(res);
res.json({ success: true})})
.catch((err) => next(err));
}
csr.service.js
const config = require("config.json");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");
const db = require("_helpers/db");
const crypto = require("../_helpers/crypto");
// var crypto = require("crypto");
var assert = require("assert");
var ObjectId = require("mongodb").ObjectId;
const { exec } = require("child_process");
const multer = require("multer");
const Csr = db.Csr;
module.exports = {
fileupload
};
async function fileupload() {
var storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, "uploads/");
},
filename: function(req, file, cb) {
cb(null, `${Date.now()}_${file.originalname}`);
},
fileFilter: function(req, file, cb) {
const ext = path.extname(file.originalname);
if (ext !== ".jpg" || ext !== ".png") {
return cb(res.status(400).end("only jpg, png are allowed"), false);
}
cb(null, true);
},
});
var upload = multer({ storage: storage }).single("file");
var csr = new Csr();
await csr.save();
console.log(upload)
return upload
}
Please suggest what is wrong in this code when I return console.log(upload) in console I got this [Function: multerMiddleware] and console.log(storage) returning like this DiskStorage {
getFilename: [Function: filename],
getDestination: [Function: destination] }
Please some one suggest me the solution.
Thank you

Problem with upload file in expo react native

Hi i tried to upload file with expo:
the console.log give me this error "Streaming file uploads via req.file() are only available over HTTP with Skipper" but when i tried it with postman it's workd.I use sailsJs for my back-end
this is my code for my expo react
const[imageUri, setImageUri]=useState()
const[imageUpload, setImageUpload]=useState()
const selectImage = async()=>{
try {
const result = await ImagePicker.launchImageLibraryAsync();
if(!result.cancelled)
setImageUri(result.uri)
setImageUpload(result)
} catch (error) {
console.log('Error running on image')
}
}
const handleSubmit = async () => {
const dataDemande = {
projectTitle: titre,
material: materiel,
description: description,
projectType: checked,
lieuRendezvous: value_3,
idClient: props.route.params.id,
model: imageUpload
};
console.log(dataDemande);
var res = await demandeClient(dataDemande);
console.log(res);
props.navigation.navigate("Tableau de bord - Client");
};
and this is the code from the back-end
findCouturierAndCreateDemande: async function (req, res) {
var recherche = req.body
req.file('model').upload({
adapter: require('skipper-gridfs'),
uri: 'mongodb://localhost:27017/image'
}, async function (err, filesUploaded) {
if (err) return res.serverError(err);
console.log(filesUploaded[0].fd)
var couturier = await Client.find({
where: {
isCouturier: true,
ville: { contains: recherche.ville },
adresse: { contains: recherche.adresse },
rue: { contains: recherche.rue },
},
})
var img = await Image.create(_.omit(filesUploaded[0], ['status', 'field', 'extra'])).fetch()
var newDemande = await Demande.create({
titre: recherche.projectTitle,
materiel: recherche.material,
description: recherche.description,
service: recherche.projectType,
positionActivity: recherche.lieuRendezvous,
client: recherche.idClient,
modelImage: img.id
}).fetch()
console.log(img)
return res.ok({couturier, newDemande})
});
}

creating array field in mongodb using mongoose

I am trying to create a collection in mongodb where a field named lists will contain an array of link and linkName. I am successfully able to create a two seperate field link and linkName, however not able to store the value inside lists.
Model code for mongodb :-
const socialSchema = new Schema({
lists: [{
link:{ formType: String},
linkName: { formType: String}
}]
})
API code :-(this code is for creating only, will later on try to use findOneAndUpdate to update the existing field
router.route('/', [auth]).post(async (req, res) => {
const {linkName, link } = req.body
try {
console.log(req.body)//Ex. { linkName: 'facebook', link: 'www.facebook.com'}
const social = new Social({
//Stuck here!!!
})
await social.save()
res.json(social)
} catch (err) {
console.error(err.message);
res.status(500).send('Server Errors')
}
}
)
Part of frontend Code(React)
const [formData, setFormData] = useState({
linkName: '',
link: ''
});
const {linkName, link} = formData
const onChange = e =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const handleSubmit = async e => {
e.preventDefault()
const socialList = {
linkName,
link
}
try {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify(socialList)
const res = await Axios.post('/api/social', body, config)
console.log(res)
} catch (err) {
console.error(err);
}
}
In your schema change from {formType: String} to {type: String}.
const data = {link: req.body.link, linkName: req.body.linkName};
Social.create({
links: [data]
});
This should work.
MY FULL WORKING CODE THAT I TESTED
const schema = new mongoose.Schema({
links: [
{
link: { type: String },
linkName: { type: String }
}
]
});
const Model = mongoose.model("test", schema);
const doc = { link: "link", linkName: "linkname" };
Model.create({
links: [doc]
});

Resources