I will teach you the quickest and easiest way to upload single as well as multiple files on AWS using React js, Node js and Express js
Here are the steps you will take:
1-Create an account on console.aws.amazon.com
2-Create a user and a new bucket. Note the accessKeyId and the secretAccessKey
3-Add policy to the bucket and the user, add CORS to the bucket.
4-Download the react-node-boilerplate github link ( link below )
5-Install aws-sdk, multer-s3, multer, path npm modules
6-Create form and api for file uploads.
7-Use axios to send the request to your api for uploading file and you will get the filename and the url location of the file(s) uploaded on aws.
Tutorial Video:
Step-1: Create an account on console.aws.amazon.com
Step 2 and 3: To create bucket and user on AWS please follow my blog on below link and then come back to this blog.
https://medium.com/@imranhsayed/how-to-create-a-user-and-bucket-amazon-web-services-aws-40631416e65
Step-4: Download the react-node-boilerplate from the below github link ( or use your own ):
https://github.com/imranhsayed/react-node-boilerplate
cd react-node-boilerplate
npm cache clean --force // in root dir
npm install
cd client
npm cache clean --force
npm install // in client directory
// to start the server
npm run dev
Step 5: Install the required npm modules
cd react-node-boilerplate
npm i aws-sdk multer-s3 multer path url --save
Step 6: Create a directory route, inside which create another directory api inside of which create a file profile.js and then write below code:
// route/api/profile.js
const express = require( 'express' );
const aws = require( 'aws-sdk' );
const multerS3 = require( 'multer-s3' );
const multer = require('multer');
const path = require( 'path' );
const url = require('url');
/**
* express.Router() creates modular, mountable route handlers
* A Router instance is a complete middleware and routing system; for this reason, it is often referred to as a “mini-app”.
*/
const router = express.Router();
/**
* PROFILE IMAGE STORING STARTS
*/
const s3 = new aws.S3({
accessKeyId: 'AKIAI4IYUCNFNIWHMB4Q',
secretAccessKey: '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',
Bucket: 'onclick'
});
/**
* Single Upload
*/
const profileImgUpload = multer({
storage: multerS3({
s3: s3,
bucket: 'onclick',
acl: 'public-read',
key: function (req, file, cb) {
cb(null, path.basename( file.originalname, path.extname( file.originalname ) ) + '-' + Date.now() + path.extname( file.originalname ) )
}
}),
limits:{ fileSize: 2000000 }, // In bytes: 2000000 bytes = 2 MB
fileFilter: function( req, file, cb ){
checkFileType( file, cb );
}
}).single('profileImage');
/**
* Check File Type
* @param file
* @param cb
* @return {*}
*/
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!' );
}
}
/**
* @route POST api/profile/business-img-upload
* @desc Upload post image
* @access public
*/
router.post( '/profile-img-upload', ( req, res ) => {
profileImgUpload( req, res, ( error ) => {
// console.log( 'requestOkokok', req.file );
// console.log( 'error', error );
if( error ){
console.log( 'errors', error );
res.json( { error: error } );
} else {
// If File not found
if( req.file === undefined ){
console.log( 'Error: No File Selected!' );
res.json( 'Error: No File Selected' );
} else {
// If Success
const imageName = req.file.key;
const imageLocation = req.file.location;
// Save the file name into database into profile model
res.json( {
image: imageName,
location: imageLocation
} );
}
}
});
});
// End of single profile upload
/**
* BUSINESS GALLERY IMAGES
* MULTIPLE FILE UPLOADS
*/
// Multiple File Uploads ( max 4 )
const uploadsBusinessGallery = multer({
storage: multerS3({
s3: s3,
bucket: 'onclick',
acl: 'public-read',
key: function (req, file, cb) {
cb( null, path.basename( file.originalname, path.extname( file.originalname ) ) + '-' + Date.now() + path.extname( file.originalname ) )
}
}),
limits:{ fileSize: 2000000 }, // In bytes: 2000000 bytes = 2 MB
fileFilter: function( req, file, cb ){
checkFileType( file, cb );
}
}).array( 'galleryImage', 4 );
/**
* @route POST /api/profile/business-gallery-upload
* @desc Upload business Gallery images
* @access public
*/
router.post('/multiple-file-upload', ( req, res ) => {
uploadsBusinessGallery( req, res, ( error ) => {
console.log( 'files', req.files );
if( error ){
console.log( 'errors', error );
res.json( { error: error } );
} else {
// If File not found
if( req.files === undefined ){
console.log( 'Error: No File Selected!' );
res.json( 'Error: No File Selected' );
} else {
// If Success
let fileArray = req.files,
fileLocation;
const galleryImgLocationArray = [];
for ( let i = 0; i < fileArray.length; i++ ) {
fileLocation = fileArray[ i ].location;
console.log( 'filenm', fileLocation );
galleryImgLocationArray.push( fileLocation )
}
// Save the file name into database
res.json( {
filesArray: fileArray,
locationArray: galleryImgLocationArray
} );
}
}
});
});
// We export the router so that the server.js file can pick it up
module.exports = router;
Add this in server.js
const profile = require( './routes/api/profile' );
app.use( '/api/profile', profile );
In Home.js for single and multiple file upload add the below code
import React, { Component } from 'react';
import axios from 'axios';
import $ from 'jquery';
class Home extends Component {
constructor( props ) {
super( props );
this.state = {
selectedFile: null,
selectedFiles: null
}
}
singleFileChangedHandler = ( event ) => {
this.setState({
selectedFile: event.target.files[0]
});
};
multipleFileChangedHandler = (event) => {
this.setState({
selectedFiles: event.target.files
});
console.log( event.target.files );
};
singleFileUploadHandler = ( ) => {
const data = new FormData();
// If file selected
if ( this.state.selectedFile ) {
data.append( 'profileImage', this.state.selectedFile, this.state.selectedFile.name );
axios.post( '/api/profile/profile-img-upload', data, {
headers: {
'accept': 'application/json',
'Accept-Language': 'en-US,en;q=0.8',
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
}
})
.then( ( response ) => {
if ( 200 === response.status ) {
// If file size is larger than expected.
if( response.data.error ) {
if ( 'LIMIT_FILE_SIZE' === response.data.error.code ) {
this.ocShowAlert( 'Max size: 2MB', 'red' );
} else {
console.log( response.data );
// If not the given file type
this.ocShowAlert( response.data.error, 'red' );
}
} else {
// Success
let fileName = response.data;
console.log( 'fileName', fileName );
this.ocShowAlert( 'File Uploaded', '#3089cf' );
}
}
}).catch( ( error ) => {
// If another error
this.ocShowAlert( error, 'red' );
});
} else {
// if file not selected throw error
this.ocShowAlert( 'Please upload file', 'red' );
}
};
multipleFileUploadHandler = () => {
const data = new FormData();
let selectedFiles = this.state.selectedFiles;
// If file selected
if ( selectedFiles ) {
for ( let i = 0; i < selectedFiles.length; i++ ) {
data.append( 'galleryImage', selectedFiles[ i ], selectedFiles[ i ].name );
}
axios.post( '/api/profile/multiple-file-upload', data, {
headers: {
'accept': 'application/json',
'Accept-Language': 'en-US,en;q=0.8',
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
}
})
.then( ( response ) => {
console.log( 'res', response );
if ( 200 === response.status ) {
// If file size is larger than expected.
if( response.data.error ) {
if ( 'LIMIT_FILE_SIZE' === response.data.error.code ) {
this.ocShowAlert( 'Max size: 2MB', 'red' );
} else if ( 'LIMIT_UNEXPECTED_FILE' === response.data.error.code ){
this.ocShowAlert( 'Max 4 images allowed', 'red' );
} else {
// If not the given ile type
this.ocShowAlert( response.data.error, 'red' );
}
} else {
// Success
let fileName = response.data;
console.log( 'fileName', fileName );
this.ocShowAlert( 'File Uploaded', '#3089cf' );
}
}
}).catch( ( error ) => {
// If another error
this.ocShowAlert( error, 'red' );
});
} else {
// if file not selected throw error
this.ocShowAlert( 'Please upload file', 'red' );
}
};
// ShowAlert Function
ocShowAlert = ( message, background = '#3089cf' ) => {
let alertContainer = document.querySelector( '#oc-alert-container' ),
alertEl = document.createElement( 'div' ),
textNode = document.createTextNode( message );
alertEl.setAttribute( 'class', 'oc-alert-pop-up' );
$( alertEl ).css( 'background', background );
alertEl.appendChild( textNode );
alertContainer.appendChild( alertEl );
setTimeout( function () {
$( alertEl ).fadeOut( 'slow' );
$( alertEl ).remove();
}, 3000 );
};
render() {
return(
<div>
<div className="container">
{/* For Alert box*/}
<div id="oc-alert-container"></div>
{/* Single File Upload*/}
<div className="card border-light mb-3 mt-5" style={{ boxShadow: '0 5px 10px 2px rgba(195,192,192,.5)' }}>
<div className="card-header">
<h3 style={{ color: '#555', marginLeft: '12px' }}>Single Image Upload</h3>
<p className="text-muted" style={{ marginLeft: '12px' }}>Upload Size: 250px x 250px ( Max 2MB )</p>
</div>
<div className="card-body">
<p className="card-text">Please upload an image for your profile</p>
<input type="file" onChange={this.singleFileChangedHandler}/>
<div className="mt-5">
<button className="btn btn-info" onClick={this.singleFileUploadHandler}>Upload!</button>
</div>
</div>
</div>
{/* Multiple File Upload */}
<div className="card border-light mb-3" style={{ boxShadow: '0 5px 10px 2px rgba(195,192,192,.5)' }}>
<div className="card-header">
<h3 style={{ color: '#555', marginLeft: '12px' }}>Upload Muliple Images</h3>
<p className="text-muted" style={{ marginLeft: '12px' }}>Upload Size: 400px x 400px ( Max 2MB )</p>
</div>
<div className="card-body">
<p className="card-text">Please upload the Gallery Images for your gallery</p>
<input type="file" multiple onChange={this.multipleFileChangedHandler}/>
<div className="mt-5">
<button className="btn btn-info" onClick={this.multipleFileUploadHandler}>Upload!</button>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Home;
When you upload the pictures you will get the response in form of file name and location url which you can save into the database and then use that url to display the images on your project .. The images will be store on aws bucket of yours and the url will be used to display the individual images.