S3 creating empty files when uploading a file from React contact form using Axios

Viewed 872

I have created a test contact form where a user can upload an image attachment.

  • The presignedURL AWS Lambda function works fine
  • The image file (blob) is showing up as an image in the html, so it's added correctly
  • When image is posted, I get a successful 200 response and the file appears in S3

However, in S3 all files appear empty. The size seems to change depending on the length of the link size (e.g. blob is 63B, a hard-coded link varies in size), so it seems like it just takes the link as data, not the actual image. I checked many times if my axios syntax is correct and went through numerous threads here in SO but can't figure out what am I doing wrong.

AWS Lambda to generate the presigned URL:

const AWS = require('aws-sdk')
AWS.config.update({ region: 'eu-west-2' })
const s3 = new AWS.S3();

const uploadBucket = 'xxx-bucket'

exports.handler = async (event) => {
  const result = await getUploadURL()
  console.log('Result: ', result)
  return result
};

const getUploadURL = async function() {
  console.log('getUploadURL started')
  let actionId = Date.now()

  var s3Params = {
    Bucket: uploadBucket,
    Key:  `${actionId}.jpg`,
    ContentType: 'image/jpeg',
    CacheControl: 'max-age=31104000',
    ACL: 'public-read'
  };

  return new Promise((resolve, reject) => {
    // Get signed URL
    let uploadURL = s3.getSignedUrl('putObject', s3Params)
    resolve({
      "statusCode": 200,
      "isBase64Encoded": false,
      "headers": {
        "Access-Control-Allow-Origin": "*"
      },
      "body": JSON.stringify({
          "uploadURL": uploadURL,
          "photoFilename": `${actionId}.jpg`
      })
    })
  })
}

React Frontend:

class QuoteForm extends Component {

  constructor(props) {
    super(props);
    this.state = {
      file:null,
    };
  };

  onContactNameChange(e){
    this.setState({ContactName:e.target.value});
  };

  onFileChange(e){
    let [file] = e.target.files;
    if (file) {
      this.setState({
        file: URL.createObjectURL(e.target.files[0]),
      });
      console.log(file);
    }
  };

  async handleSubmit(e){
    e.preventDefault();
  
      axios.post(`https://xxxx/`)
      .then(res => {
            console.log(res)
            const url = res.data.uploadURL;

            const axiosConfig = {
                headers: { 
                    "Content-Type": "image/jpeg",
                    "Cache-Control": 'max-age=31104000' 
                }
            }

            axios.put(url, this.state.file, axiosConfig)
                .then(response => {console.log(response)}).catch(err => {console.log(err.response)
            });
        });

    }
  
  render() {
    return (
      <form onSubmit={this.handleSubmit.bind(this)}>
            <input 
                type="file"
                name="file"
                id="fileUpload" 
                onChange={this.onFileChange.bind(this)}
            />

            <img src={this.state.file} alt="img" />

            <button 
                className="large"
                style={{marginTop:"1.25rem"}} >
                Submit
            </button>
      </form>
    );
  }
}

S3 Bucket files: enter image description here

I just can't understand why the files in S3 bucket turn up empty.

1 Answers

Ok, I figured out what was my issue. createObjectURL in my frontend React code apparently can't be used to upload files to server since it doesn't actually include the file, but the URL only. I shortened the whole onFileChange(e) function to the one below and S3 started receiving the actual files.

  onFileChange(e){
        const [selectedFile] = e.target.files;
    if (selectedFile) {
            this.setState({file:selectedFile})
        }
  };
Related