how can i upload multiple images to amazon s3
this is my middleware for image upload
const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");
aws.config.update({
secretAccessKey: process.env.AWSSecretKey,
accessKeyId: process.env.AWSAccessKeyId
});
const s3 = new aws.S3();
const upload = multer({
storage: multerS3({
s3: s3,
bucket: 'ajibade',
acl: 'public-read',
metadata: (req, file, cb) => {
cb(null, { fieldName: file.fieldname });
},
key: (req, file, cb) => {
cb(null, Date.now().toString())
}
})
})
module.exports = upload;
this is how i upload the file
<label class="choosefile-button">
<input
type="file"
#change="onFileSelected1"
>
<p style="margin-top: -70px">{{ fileName }}</p>
</label>
<label class="choosefile-button">
<input
type="file"
#change="onFileSelected2"
>
<p style="margin-top: -70px">{{ fileName }}</p>
</label>
how i call the function
methods: {
onFileSelected1(event) {
this.selectedFile = event.target.files[0]
console.log(this.selectedFile)
this.fileName = event.target.files[0].name
},
onFileSelected2(event) {
this.selectedFile = event.target.files[0]
console.log(this.selectedFile)
this.fileName = event.target.files[0].name
},
}
each time i click on either onFileSelected1 or onFileSeleted2 it uploads the same images for the both inputs
this is my post request
router.post(`/products`, upload.single("photo"), async (req, res) => {
console.log(res);
try {
let product = new Product();
product.photo = req.file.location;
await product.save();
} catch (error) {
console.log(error);
}
});
please how can i go about this
This blog post explains quite in depth how it's done.
Basically what you need to do is add a <input type="file" multiple /> and in your upload function turn it into multipart/form-data. Then when you upload it to your backend, you use multer and multer-s3 to send it to S3.
I think you've mixed up a few concepts here, your upload function looks like a route that is supposed to be in the backend of the application, where you then send the request to upload files using for example axios or the fetch API.
Related
I created an image upload API using Multer. The image is also stored in my file and responds back to the path of the image. But I can't preview this image on the browser and also on the frontend.
I save an image in my local folder and get a response as the path of the image. I saved the path in the MongoDB database and want to fetch the image from the database.
//upload router
import express from "express";
import multer from "multer";
import path from "path";
import { Request, Response } from "express";
const router = express.Router();
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads/");
},
filename: function (req, file, cb) {
cb(
null,
`${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`
);
},
});
const upload = multer({ storage: storage });
router.post("/", upload.single("image"), (req: any, res: Response) => {
const imgUrl = req.file.path.replace(/\\/g, "/");
console.log(imgUrl);
res.send(`/${imgUrl}`);
});
export default router;
//server.js
app.use("/api/v1/uploads", imageUpload);
app.use("/uploads", express.static(path.join(__dirname, "/uploads")));
//image input
const uploadImgHandler = async (e: any) => {
const file = e.target.files[0];
const formData = new FormData();
formData.append("image", file);
setUploadingImg(true);
try {
const config = {
headers: {
"Content-Type": "multipart/form-data",
},
};
const { data } = await axios.post(
"http://localhost:5000/api/v1/uploads",
formData,
config
);
console.log("data", data);
setEmployeeImg(data);
setUploadingImg(false);
} catch (error) {
console.error(error);
setUploadingImg(false);
}
};
// input jsx
<input
accept="image/*"
name="image"
id="contained-button-file"
type="file"
style={{ padding: 10 }}
onChange={uploadImgHandler}
/>
//fetch image
<div>
<p>Address: {profile?.address}</p>
<p>Salary: {profile?.salary}</p>
<p>Joinning Date: {profile?.joingDate}</p>
<p>image</p>
<img
src={`http://localhost:5000/api/v1/uploads${profile?.image}`}
alt=""
style={{ height: "10rem", width: "10rem" }}
/>
</div>
image path/ profile.image value
browser screenshot
Image stored in my folder
Maybe I make a mistake somewhere but can't find out. But I think, there is something wrong with the static path. But I didn't find anything wrong.
In the browser, the error is as the bellow
You can try this:
//server.js
const dirname = path.resolve();
app.use('/uploads', express.static(path.join(dirname, '/uploads')));
//fetch image
<div>
<p>Address: {profile?.address}</p>
<p>Salary: {profile?.salary}</p>
<p>Joinning Date: {profile?.joingDate}</p>
<p>image</p>
<img
src={`http://localhost:5000${profile?.image}`}
alt=""
style={{ height: "10rem", width: "10rem" }}
/>
</div>
Im trying to single file upload to folder public/files, using reactJS as frontend, multer nodeJS as serverside :
react :
here i have file input that accepts only pdf and msword, and on change it sets the file state, on submit it triggers function handleCareer which makes formData object and appends data from state, the file.name will be something like 1663010450031report.pdf, and then make request to /upload.
const [file, setFile] = useState(null);
const handleCareer = async () => {
const data = new FormData();
const pdf = `${Date.now()}${file.name}`;
data.append("file", pdf);
try {
await fetch("http://localhost:8000/upload", {
method: "POST",
body: data,
});
} catch (err) {
console.log(err);
}
};
<div className="uploadCV">
<label htmlFor="resumeFile" className="downloadImg">
<AddAPhotoIcon className="postIcon" />
<span>Upload resume</span>
</label>
<input
type="file"
accept="application/pdf,application/msword"
id="resumeFile"
name="file"
onChange={(e) => setFile(e.target.files[0])}
></input>
</div>
<div className="submitUpload">
<button type="button" onClick={handleCareer}>
Request
</button>
</div>
server :
here im using path public/files as the target folder for upload and specifying in destination as well as specifying req.body.file for filename
app.use("/files", express.static(path.join(__dirname, "public/files")));
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "public/files");
},
filename: (req, file, cb) => {
cb(null, req.body.file);
},
});
const upload = multer({ storage: storage }).single("file");
app.post("/upload", function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
console.log(err);
} else if (err) {
console.log(err);
}
return res.status(200).send(req.file);
});
});
but the file is not uploading, and it doesn't console log any errors, on postman it gives empty result with status code 200 .. what's is the problem here ?
You haven't send the file field to backend. You have to send a file (like pdf). Now you are sending only a pdf name. There aren't any file.
const upload = multer({ storage: storage }).single("file");
Here you have defined the name of incoming data is file. So you should add the field which named file
please how do I tell my view to display the image in the view with ejs template.
I am uploading to Amazon multers3 and the image is being uploaded in this directories successfully public/uploads/postImages
this is my upload inside my route file.
const upload = multer({
storage: multerS3({
s3: s3,
bucket: bucketname,
s3BucketEndpoint:true,
endpoint:"http://" + bucketname + ".s3.amazonaws.com",
key: function (req, file, cb) {
cb(null, 'public/uploads/postImages/' + file.originalname);
}
})
})
router.post('/', upload.single('cover'), async (req, res, next) => {
const fileName = req.file != null ? req.file.filename : null
const post = new Post({
title: req.body.title,
description: req.body.description,
from: req.body.from,
postImage: fileName,
})
try {
const newPost = await post.save()
res.redirect('/posts')
} catch {
if (post.postImage != null) {
removePostImage(post.postImage)
}
renderNewPage(res, post, true)
}
});
this is my posts/index.ejs
<main role="main">
<ul class="flexgrid columns-news">
<% posts.forEach(post => { %>
<li>
<figure class="gallery-image">
<img src="/public/uploads/postImages/<%= post.postImage %>" >
</figure>
</li>
<% });%>
</ul>
</main>
How do I tell my view to look for the image inside public/uploads/postImages ?
I have installed multer, multer-s3 and aws-sdk and try to upload a profile image when a user upload a profile picture.
so far I have the code below in a file call file_upload.js
const aws = require('aws-sdk')
const multer = require('multer')
const multerS3 = require('multer-s3')
aws.config.update({
secretAccessKey: "my access key",
accessKeyId: "my key id",
region: "us-east-2"
})
const s3 = new aws.S3()
const upload = multer({
storage: multerS3({
s3: s3,
bucket: 'user-image1',
acl: 'public-read',
metadata: function (req, file, cb) {
cb(null, {fieldName: 'TESTING-META-DATA'});
},
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
module.exports = upload
and a post request when user choose some input
const upload = require('../assets/file-upload')
const singleUpload = upload.single('image')
router.post("/edit/:id", (req, res) => {
User.findByIdAndUpdate(
{ _id: id },
{
$set: {
education: req.body.education,
language: req.body.language,
bio: req.body.bio,
image_file: req.body.image_file
},
},
{ new: true },
(err, result) => {
res.redirect("/dashboard");
}
);
});
in my ejs, all the String input was handle correctly when user input the data, except for image.
<div class="upload-btn-img">
<input type="file" name="image_file" onchange="showThumbnail(this)" />
</div>
My question is, how can i save a photo with all the other input? all the other input is store in mongodb atlas, and images needs to store in amazon s3. the url need to be store in mongodb atlas under field named image_file.
You would need a middleware that intercept the request and send the file to s3, which then returns the url.
Here is an example i did a while back this is the
--Controller https://github.com/C0D1NGD0J0/weareunited4life/blob/master/app/Controllers/upload.js)
--Route
router.post("/", passport.authenticate('jwt', {session: false}), uploadImg, postCntrl.create);
Pay attention to the "uploadImg" middleware, it must come before the controller action that save the document.
i need a help on how to upload multiple files image on amazon S3. i let's say that i have three input like this
<template lang="html">
<form enctype="multipart/form-data" #submit.prevent="sendFile">
<input type="file" ref="file1" #change="selectFile1">
<input type="file" ref="file2" #change="selectFile2">
<input type="file" ref="file3" #change="selectFile3">
<div class="field">
<button class="button is-info">Send</button>
</div>
</form>
</template>
and my frontend vue script looks like this
<script>
import _ from 'lodash'
export default {
name: 'BulkUpload',
data() {
return {
file: "",
message: "",
error: false,
sendFiles: []
}
},
methods: {
selectFile1() {
this.file = this.$refs.file1.files[0]
this.sendFiles.push(this.file)
console.log(this.sendFiles);
},
selectFile2() {
this.file = this.$refs.file2.files[0]
this.sendFiles.push(this.file)
console.log(this.sendFiles);
},
selectFile3() {
this.file = this.$refs.file3.files[0]
this.sendFiles.push(this.file)
console.log(this.sendFiles);
},
sendFile() {
let formData = new FormData()
_.forEach(this.uploadFiles, file => {
formData.append('file', file)
})
var self = this
axios.post('http://localhost:3000/image/bulkupload', formData)
.then(function(response) {
console.log(response);
self.message = "File has been uploaded"
self.file = ""
self.error = false
})
.catch(function(err) {
self.message = "Something went wrong"
self.error = true
console.log(err);
})
}
}
}
</script>
after sending it in using formData to the express server, my image route will handle it with this code
const sendingImage = require('../helper/sendingImage');
router.post('/bulkupload', sendingImage.upload.array("file"), (req, res) => {
var promises=[];
for(var i = 0; i < req.files.length; i++) {
var file = req.files[i];
promises.push(sendingImage.uploadLoadToS3(file));
}
Promise.all(promises).then(function(data){
res.send('Uploadedd');
console.log('success');
}).catch(function(err) {
console.log('failed');
res.send(err.stack);
})
})
and my helper looks like this
const multer = require('multer');
const aws = require('aws-sdk');
aws.config.update({
accessKeyId: <my access id>,
secretAccessKey: <my secret key>
})
const upload = multer({
dest: './uploads/'
})
function uploadLoadToS3(ObjFile){
var params = {
ACL :'public-read',
Body : new Buffer(ObjFile.buffer),
Bucket:'vue-express-upload',
ContentType:ObjFile.mimetype,
Key:ObjFile.originalname
}
return s3.upload(params).promise();
}
module.exports = {
upload,
uploadLoadToS3
}
to be honest, my code works without any problem. it send response status 200 without any error.
but the problem is, when i check my S3 bucket, it doesn't have any uploaded file in it. it's still empty.
is there anything wrong with my code ?
The solution I found for using aws-sdk. The good that I will also use this solution and my projects.
Credits: StackOver - Answer
Reference: Multer-s3
In your helper file you'll leave it like this:
const aws = require('aws-sdk')
const multer = require('multer')
const multerS3 = require('multer-s3')
aws.config = new aws.Config({
accessKeyId: <my access id>,
secretAccessKey: <my secret key>,
region: <my region>
})
const s3 = new aws.S3()
const upload = multer({
storage: multerS3({
s3: s3,
acl: 'public-read',
bucket: 'vue-express-upload',
contentType: function(req, file,cb){
cb(null, file.mimetype)
},
key: function (req, file, cb) {
cb(null, file.originalname)
}
})
})
module.exports = upload
The key of the file you can also change to something more unique, like the date / minutes / seconds. It is at your discretion, I used the filename as key.
In your router the route will look like this:
const upload = require('../helper/sendingImage')
router.post('/bulkupload', upload.array('files'), (req, res) => {
console.log('success')
//console.log(req.files)
res.send('Uploadedd')
//res.send(req.files)
})
To view upload data:
router.post('/bulkupload', upload.array('files'), (req, res) => {
res.send({
status: 200,
data: {
sucess: true,
file: req.files
}
})
})
If there is no middleware file, it simply will not send. Will not return error. The only errors that can return are those of AWS access information.
Note:I used the postman.