Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to upload image to aws s3 using react and django rest framework

I am trying to upload image to aws s3 using react and drf. I am following this heroku documentation . I am getting error mentioning Request failed with status code 400.

as they have suggested in the link first I created signed url from backend.

urlpatterns = [ 
   path('api/create-permission/aws-s3/', SignS3Upload.as_view(), name='aws-s3'),    
]

import os
import boto3
import mimetypes
s3 = boto3.client('s3')

class SignS3Upload(APIView):
    # authentication_classes = (authentication.SessionAuthentication,)
    # permission_classes = [IsAuthenticated, ]

    def get(self, request):
        s3_bucket = os.environ.get('AWS_STORAGE_BUCKET_NAME')
        file_name = request.GET['image_name']
        file_type = mimetypes.guess_type(file_name)[0]
        presigned_post = s3.generate_presigned_post(
            Bucket=s3_bucket,
            Key=file_name,
            Fields={"acl": "public-read", "Content-Type": file_type},
            Conditions=[
                {"acl": "public-read"},
                {"Content-Type": file_type}
            ],
            ExpiresIn=3600
        )

        data = {
            "signed_url": presigned_post,
            'url': 'https://%s.s3.amazonaws.com/%s' % (s3_bucket, file_name)
        }
        return Response(data) 

In frontend I am using React and Redux. here how I am sending request from frontend

export const getSignedRequest = (image) => (dispatch, getState) => {

    const image_name = image.name

    axios.get('https://my-site.herokuapp.com/api/blog/api/create-permission/aws-s3/', { params: { image_name } })
        .then((res) => {
            dispatch({
                type: GET_PARTICULAR_BLOG_IMG_UPLOAD,
                payload: res.data
            });


            var postData = new FormData();
            for (key in res.data.fields) {
                postData.append(key, res.data.fields[key]);
            }
            postData.append('file', image_name);

            return axios.post(res.data.url, postData);
        })
        .then((res) => {
            dispatch({
                type: GET_PARTICULAR_BLOG_IMG_UPLOAD_AWS,
                payload: res.data
            });
        })
        .catch((err) => {
            console.log(err)
        });

};

I received the response from first axios request in frontend like below

signed_url: {
  url: 'https://my-site.s3.amazonaws.com/',
  fields: {
    acl: 'public-read',
    'Content-Type': 'image/jpeg',
    key: 'xxxxxxxxxxx.jpg',
    AWSAccessKeyId: 'xxx_access_id_xxxx',
    policy: 'xxx_policy_xxx',
    signature: 'xxx_signature_xxx'
  }
},
url: 'https://my-site.s3.amazonaws.com/xxxxxxxxxxx.jpg'

},

here is settings.py

AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME')
AWS_S3_FILE_OVERWRITE = False
AWS_DEFAULT_ACL = None
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
like image 282
ConAra Avatar asked Nov 20 '25 11:11

ConAra


1 Answers

I also ran into the same problem. Here is fix I got for you.

your backend django code should look like this.

import boto3

def create_presigned_url(
    object_name,
    object_type,
    bucket_name=settings.BUCKET_NAME,
    expiration=3600
):


    # Generate a presigned S3 POST URL
    s3_client = boto3.client(
        's3',
        aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
        aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
        region_name=settings.AWS_REGION_NAME,
    )

        response = s3_client.generate_presigned_url(
            'put_object',
            {
                'Bucket': bucket_name,
                'Key': object_name,
                'ACL': 'public-read',
                'ContentType': object_type
            },
            ExpiresIn=expiration,
        )
    
    return {
        "signedRequest": response,
        'url': f'https://{bucket_name}.s3.{settings.AWS_REGION_NAME}.amazonaws.com/{object_name}'
    }

object name and the object type is the file name and file type contained in your request object.

The code at your frontend should be like this

import axios from "axios";

const fileHandler = (file) => {
    let response = await getSignedUrl({
      fileName: file.name,
      fileType: file.type,
    });
    let { signedRequest, url } = response.data || {};

    // fileType in the headers for the upload
    let options = { headers: { "Content-Type": file.type } };

    let instance = axios.create();
    instance.put(signedRequest, file, options);
}

The url key from the response object is your image url after sending a put request to signedRequest

like image 145
ikrma ahmad Avatar answered Nov 23 '25 02:11

ikrma ahmad