I am having an issue uploading a pdf to s3 bucket from my react app. I get an error when i try to push the file but the error is not very clear.
this is how I am generating the presigned_url on my flask app:
from flask import Flask, jsonify, request
import boto3
import datetime
def create_file_upload_url_controller():
try:
s3_client = boto3.client('s3')
print('create_file_upload_url_controller()')
user_id = str(request.user.id)
file_name = f"{user_id}_{datetime.datetime.utcnow()}"
bucket_name = "docwise-large-files-dev"
print(f'file_name: {file_name}')
# Generate pre-signed URL for upload
presigned_url = s3_client.generate_presigned_url(
'put_object',
Params={'Bucket': bucket_name, 'Key': file_name},
ExpiresIn=3600, # URL valid for 1 hour
HttpMethod="PUT"
)
print(f'presigned_url: {presigned_url}')
return jsonify({'upload_url': presigned_url, 'file_name': file_name})
except Exception as e:
print(f'e: {e}')
return jsonify({'error': str(e)}), 500
this is the component that collects the file from the user::
// FileUpload.js
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { addFile } from '../../../../../../actions/filesActions';
import { useNavigate, useParams } from 'react-router-dom';
const FileUpload = ({ onFileChange, addFile, conversation_id,files }) => {
const { chatId } = useParams();
useEffect( ()=>{
},[files])
const handleFileChange = (event) => {
const selectedFiles = event.target.files;
for (let i = 0; i < selectedFiles.length; i++) {
const file = selectedFiles[i];
const fileDetails = {
conversation_id: chatId,
file: file,
};
addFile(fileDetails);
}
};
return (
<div>
<input type="file" onChange={handleFileChange} />
</div>
);
};
const mapStateToProps = (state) => ({
files: state.files.files
});
const mapActionsToProps = {
addFile,
};
export default connect(mapStateToProps, mapActionsToProps)(FileUpload);
this is the action that pushes the file to aws s3::
// filesActions.js
export const ADD_PDF = 'ADD_PDF';
export const ADD_PDF_SUCCESS = 'ADD_PDF_SUCCESS';
export const ADD_PDF_FAIL = 'ADD_PDF_FAIL';
export const DELETE_PDF = 'DELETE_PDF';
export const UPDATE_PDF = 'UPDATE_PDF';
// /api/file/create_file_upload_url_url
let baseUrl;
if (process.env.MODE === 'local') {
baseUrl = `/api/file/`;
} else {
baseUrl = `${process.env.REACT_APP_BACKEND_URL}/api/file/`;
}
export const addFile = (pdfDetails) => async (dispatch, getState) => {
try {
const { token } = getState().auth;
const createFileUploadURLResponse = await fetch(`${baseUrl}create_file_upload_url`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: null,
});
if (createFileUploadURLResponse.error != null) {
console.log('error: ', createFileUploadURLResponse.error);
return dispatch({
type: ADD_PDF_FAIL,
payload: createFileUploadURLResponse.error,
});
}
const createFileUploadURLResponseData = await createFileUploadURLResponse.json();
console.log(`createFileUploadURLResponseData: ${createFileUploadURLResponseData.upload_url}`);
console.log(`key : ${createFileUploadURLResponseData.file_name}`)
console.log('pdfDetails.file: ',pdfDetails.file);
console.log('before uploading file to s3')
const uploadFileResponse = await fetch(`${createFileUploadURLResponseData.upload_url}`, {
method: 'PUT',
headers: {
'Content-Type': pdfDetails.file.type, // Change to file's actual MIME type
'Key': `${createFileUploadURLResponseData.file_name}`,
},
body: pdfDetails.file,
});
const uploadFileResponseData = await uploadFileResponse.json();
console.log(`uploadFileResponseData: ${JSON.stringify(uploadFileResponseData)}`);
} catch (error) {
console.log('error: ', error);
return null;
}
};
This is the error that I am getting::
filesActions.js:52 PUT https://docwise-large-files-dev.s3.amazonaws.com/Juan_Casas_Resume.pdf?AWSAccessKeyId=AKIAYNCJFQSXTCQHFGR3&Signature=GTb89cRSPxDQhp0TmTfuggDKPfc%3D&Expires=1692137811 403 (Forbidden)
I found this article but It didnt help:: https://fullstackdojo.medium.com/s3-upload-with-presigned-url-react-and-nodejs-b77f348d54cc
the headers on both server and front end need to be the same.
I needed these headers in the backend;:
Params={'Bucket': bucket_name, 'Key': file_name, 'ContentType': "application/pdf",},
i was missing the content-type