Skip to content

Commit c89c3e3

Browse files
committed
Added S3 Image Upload API and Frontend
1 parent 3d56346 commit c89c3e3

File tree

16 files changed

+225
-26
lines changed

16 files changed

+225
-26
lines changed

Backend/EcommerceInventory/.env

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
DEBUG='True'
2+
# SECRET_KEY='django-insecure-n==@#u@5goj0o27k8isfo%6!k3fs(2u@3r7+n!ovu-uu1#p=gd'
3+
# DATABASE_NAME='ecommerce'
4+
# DATABASE_USER='ecommerce'
5+
# DATABASE_PASSWORD='ecommerce'
6+
# DATABASE_HOST='ecommerce.cfamkce00xa3.ap-south-1.rds.amazonaws.com'
7+
# DATABASE_PORT='3306'
28
SECRET_KEY='django-insecure-n==@#u@5goj0o27k8isfo%6!k3fs(2u@3r7+n!ovu-uu1#p=gd'
3-
DATABASE_NAME='ecommerce'
4-
DATABASE_USER='ecommerce'
5-
DATABASE_PASSWORD='ecommerce'
6-
DATABASE_HOST='ecommerce.cfamkce00xa3.ap-south-1.rds.amazonaws.com'
7-
DATABASE_PORT='3306'
9+
DATABASE_NAME='ecommerceproject'
10+
DATABASE_USER='ecommerceproject'
11+
DATABASE_PASSWORD='ecommerceproject'
12+
DATABASE_HOST='localhost'
13+
DATABASE_PORT='3306'
14+
15+
AWS_ACCESS_KEY_ID='ACCESS_KEY_ID'
16+
AWS_ACESS_KEY_SECRET='AWS_ACESS_KEY_SECRET'
17+
AWS_STORAGE_BUCKET_NAME='AWS_BUCKET_NAME'
18+
AWS_S3_REGION_NAME='AWS_S3_REGION_NAME'
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Backend/EcommerceInventory/EcommerceInventory/settings.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,9 @@
172172
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
173173
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
174174
}
175+
176+
177+
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
178+
AWS_ACESS_KEY_SECRET = os.getenv('AWS_ACESS_KEY_SECRET')
179+
AWS_STORAGE_BUCKET_NAME=os.getenv('AWS_STORAGE_BUCKET_NAME')
180+
AWS_S3_REGION_NAME=os.getenv('AWS_S3_REGION_NAME')

Backend/EcommerceInventory/EcommerceInventory/urls.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,22 @@
1717
from django.contrib import admin
1818
from django.urls import include, path, re_path
1919

20-
from EcommerceInventory.views import index
20+
from EcommerceInventory.views import index,FileUploadViewInS3
2121
from EcommerceInventory import settings
2222
from UserServices.Controller.DynamicFormController import DynamicFormController
2323
from UserServices.Controller.SuperAdminDynamicFormController import SuperAdminDynamicFormController
2424
from UserServices.Controller.SidebarController import ModuleView
2525
from django.conf.urls.static import static
2626

27+
2728
urlpatterns = [
2829
path('admin/', admin.site.urls),
2930
path('api/auth/', include('UserServices.urls')),
3031
path('api/getForm/<str:modelName>/',DynamicFormController.as_view(),name='dynamicForm'),
3132
path('api/superAdminForm/<str:modelName>/',SuperAdminDynamicFormController.as_view(),name='superadmindynamicForm'),
3233
path('api/getMenus/',ModuleView.as_view(),name='sidebarmenu'),
3334
path('api/products/',include('ProductServices.urls')),
35+
path('api/uploads/',FileUploadViewInS3.as_view(),name='fileupload')
3436
]
3537

3638
if settings.DEBUG:
Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
11
from django.shortcuts import render
2+
from rest_framework.views import APIView
3+
from rest_framework.parsers import MultiPartParser,FormParser
4+
from rest_framework.response import Response
5+
from boto3.session import Session
6+
from EcommerceInventory.settings import AWS_ACCESS_KEY_ID,AWS_ACESS_KEY_SECRET,AWS_S3_REGION_NAME,AWS_STORAGE_BUCKET_NAME
7+
import os
8+
29
def index(request):
3-
return render(request, 'index.html')
10+
return render(request, 'index.html')
11+
12+
class FileUploadViewInS3(APIView):
13+
parser_classes=(MultiPartParser,FormParser)
14+
15+
def post(self,request,*args,**kwargs):
16+
uploaded_files_urls=[]
17+
for file_key in request.FILES:
18+
file_obj=request.FILES[file_key]
19+
s3_client=Session(
20+
aws_access_key_id=AWS_ACCESS_KEY_ID,
21+
aws_secret_access_key=AWS_ACESS_KEY_SECRET,
22+
region_name=AWS_S3_REGION_NAME
23+
).client("s3")
24+
25+
uniqueFileName=os.urandom(24).hex()+"_"+file_obj.name.replace(" ","_")
26+
file_path="uploads/"+uniqueFileName
27+
28+
s3_client.upload_fileobj(
29+
file_obj,
30+
AWS_STORAGE_BUCKET_NAME,
31+
file_path,
32+
ExtraArgs={
33+
'ContentType':file_obj.content_type
34+
}
35+
)
36+
s3url=f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/{file_path}"
37+
uploaded_files_urls.append(s3url)
38+
return Response({'message':'File uploaded successfully','urls':uploaded_files_urls},status=200)
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import {useFormContext} from 'react-hook-form';
2+
import { Box,FormControl,InputLabel,Select,MenuItem, FormControlLabel, Switch, TextField,Alert, Typography, IconButton, LinearProgress } from "@mui/material";
3+
import useApi from '../hooks/APIHandler';
4+
import { useEffect,useState } from 'react';
5+
import DescriptionIcon from '@mui/icons-material/Description';
6+
import { Delete } from '@mui/icons-material';
7+
import Button from '@mui/material/Button';
8+
import { toast } from 'react-toastify';
9+
10+
const FileInputComponent = ({field}) => {
11+
const {register,formState:{errors},watch,setValue} = useFormContext();
12+
const {callApi,loading}=useApi();
13+
const [selectedFiles,setSelectedFiles]=useState([]);
14+
const [filePreviews,setFilePreviews]=useState([]);
15+
const [fileUploaded,setFileUploaded]=useState(false);
16+
17+
const handleDeleteImage=(index)=>{
18+
// Delete File Actual
19+
const updatedFiles=[...selectedFiles]
20+
updatedFiles.splice(index,1);
21+
setSelectedFiles(updatedFiles);
22+
23+
//Delete File preview
24+
const updatedPreviews=[...filePreviews];
25+
updatedPreviews.splice(index,1);
26+
setFilePreviews(updatedPreviews);
27+
28+
setFileUploaded(false);
29+
}
30+
31+
const uploadFiles=async()=>{
32+
try{
33+
const formData=new FormData();
34+
selectedFiles.forEach((file,index)=>{
35+
formData.append(`file${index}`,file);
36+
})
37+
38+
const response=await callApi({url:'uploads/',method:'post',body:formData,header:{'Content-Type':'multipart/form-data'}});
39+
setValue(field.name,JSON.stringify(response?.data?.urls));
40+
toast.success(response?.data?.message);
41+
setFileUploaded(true);
42+
}
43+
catch(err){
44+
toast.error(err?.message);
45+
}
46+
}
47+
48+
const deleteAllFiles=()=>{
49+
setSelectedFiles([]);
50+
setFilePreviews([]);
51+
setFileUploaded(false);
52+
}
53+
54+
useEffect(()=>{
55+
if(!selectedFiles.length && watch(field.name)){
56+
console.log(watch(field.name));
57+
const fileArray=Array.from(watch(field.name)) || [];
58+
setSelectedFiles(fileArray);
59+
const preview=fileArray.map((file,index)=>({
60+
url:URL.createObjectURL(file),
61+
name:file.name,
62+
type:file.type.split('/')[0]
63+
}))
64+
console.log(preview);
65+
setFilePreviews(preview);
66+
setFileUploaded(false);
67+
}
68+
},[watch(field.name)]);
69+
70+
return (
71+
<>
72+
{
73+
filePreviews.length>0 && filePreviews.map((file,index)=>(
74+
<Box key={index} sx={{display:'flex',alignItems:'center',mb:2}}>
75+
{
76+
file.type==='image'?<img src={file.url} alt={file.name} style={{width:'60px',height:'60px'}} />:<DescriptionIcon sx={{width:'60px',height:'60px'}} />
77+
}
78+
<Typography variant='body1' p={1}>{file.name}</Typography>
79+
<IconButton onClick={()=>handleDeleteImage(index)} sx={{color:'red'}}>
80+
<Delete/>
81+
</IconButton>
82+
</Box>
83+
))
84+
}
85+
{!filePreviews.length &&
86+
<Box p={1} mb={1}>
87+
<Typography variant='title'>{field.label}</Typography>
88+
<Box component={"div"} className='fileInput' mt={1}>
89+
<input type='file' multiple {...register(field.name,{required:field.required})} />
90+
</Box>
91+
{
92+
!!errors[field.name] && <Alert variant="outlined" severity='error'>
93+
This Field is Required
94+
</Alert>
95+
}
96+
</Box>
97+
}
98+
{
99+
selectedFiles.length>0 && !fileUploaded && (
100+
loading?<LinearProgress sx={{width:'100%'}}/>:
101+
<Box mt={2} display="flex" justifyContent="space-between">
102+
<Button onClick={uploadFiles} variant='contained' color='primary'>Upload Files</Button>
103+
<Button onClick={deleteAllFiles} color='primary' variant='contained'>Delete All Files</Button>
104+
</Box>
105+
)
106+
}
107+
</>
108+
)
109+
}
110+
export default FileInputComponent;

0 commit comments

Comments
 (0)