Before switching to aws s3, my mern app was uploading and displaying images using react-dropzone and multer. The image files were saved in a server-side 'uploads' folder. The app worked fine locally.
My issue started once I deployed on Heroku. For context, here's my live site: https://famcloud.herokuapp.com/
Now that I've changed a few lines of code to store the files in the aws s3 bucket, my images aren't rendering.
The good news is, through my console, I can see that the images are being uploaded and sent to the aws s3 bucket (as well as the Db). So the issue is figuring out how to render them.
I'm stuck in tutorial hell with so many youtube videos and blog articles explaining how to upload files to aws, but can't seem to find the right guidance for displaying them.
Well, I'll get to the code now.
Luckily, it's just two files where I'm missing something.
On the back-end side, my photo.js file in my Routes Folder looks like this:
const router = express.Router();
const { Photo } = require("../../models/Photo");
const multer = require('multer');
const aws = require('aws-sdk');
const multerS3 = require('multer-s3');
const path = require('path');
const { auth } = require("../../middleware/auth");
const s3 = new aws.S3({
accessKeyId: '',
secretAccessKey: '',
Bucket: 'famcloud2'
});
function checkFileType(file, cb) {
// Allowed ext
const filetypes = /jpeg|jpg|png|gif/;
// Check ext
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
const mimetype = filetypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
} else {
cb('Error: Images Only!');
}
}
// Multiple File Uploads ( max 4 )
const uploadsBusinessGallery = multer({
storage: multerS3({
s3: s3,
bucket: 'famcloud2',
acl: 'public-read',
metadata: function (req, file, cb) {
cb(null, { fieldName: file.fieldname });
},
key: function (req, file, cb) {
cb(null, path.basename(file.originalname, path.extname(file.originalname)) + '-' + Date.now() + path.extname(file.originalname))
}
}),
limits: { fileSize: 10000000 }, // In bytes: 2000000 bytes = 2 MB
fileFilter: function (req, file, cb) {
checkFileType(file, cb);
}
}).array('galleryImage', 4);
router.post('/uploadMultiple', auth, (req, res) => {
uploadsBusinessGallery(req, res, err => {
console.log('file sent/saved to AWS: ', req.files);
if (err) {
return res.json({ success: false, err })
}
return res.json({ success: true, image: req.files.key, fileName: req.files.key })
})
});
//Here's the front-end React Component file:
import React, { useState } from 'react'
import Dropzone from 'react-dropzone';
import { CloudUploadOutlined } from '@ant-design/icons'
import Axios from 'axios';
function FileUpload(props) {
const [Images, setImages] = useState([])
const onDrop = (files) => {
let formData = new FormData();
const config = {
header: {
'accept': 'application/json',
'Accept-Language': 'en-US,en;q=0.8',
'Content-Type': `multipart/form-data; boundary=${formData._boundary}`
}
}
formData.append("galleryImage", files[0])
//save the Image we chose inside the Node Server
Axios.post('/api/photo/uploadMultiple', formData, config)
.then(response => {
if (response.data.success) {
setImages([...Images, response.data.image])
props.refreshFunction([...Images, response.data.image])
} else {
alert('Failed to save the Image in Server')
}
})
}
const onDelete = (image) => {
const currentIndex = Images.indexOf(image);
let newImages = [...Images]
newImages.splice(currentIndex, 1)
setImages(newImages)
props.refreshFunction(newImages)
}
return (
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Dropzone
onDrop={onDrop}
multiple={false}
maxSize={800000000}
>
{({ getRootProps, getInputProps }) => (
<div className="uploadTarget"
style={{
width: '300px', height: '240px', border: '1px solid lightgray',
display: 'flex', alignItems: 'center', justifyContent: 'center',
backgroundColor: "rgba(1,1,1,0.3)", borderRadius: "10px"
}}
{...getRootProps()}
>
<input {...getInputProps()} />
<CloudUploadOutlined style={{ fontSize: '5rem', cursor: "pointer" }} />
</div>
)}
</Dropzone>
<div style={{ display: 'flex', width: '350px', height: '240px', overflowX: 'scroll' }}>
{Images.map((image, index) => (
<div onClick={() => onDelete(image)}>
<img style={{ minWidth: '300px', width: '300px', height: '240px' }} src={`http://localhost:5000/${image}`} alt={`photoImg-${index}`} />
</div>
))}
</div>
</div>
)
}
export default FileUpload```