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'
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With