'Error in posting file to S3 via a pre-signed URL

I am building a vue app to upload files to s3. The app makes an API call with file details which will be stored in the database and the API call will return a pre-signed URL where the file will be saved. UI to API flow works, however, the axios PUT call to save the file in S3 fails.

Step 1: Call the API (SUCCESS) Step 2: Save the file details and return a pre-signed URL (SUCCESS) Step 3: upload the file via axios (or Postman) (FAIL)

The web code is:

    var fileName = uuidv4() + ".webm";

    var apiData = {
      body: {
        data: {
          recordedBy: username,
          fileName: fileName,
          fileType: mediaType,
        },
      },
    };

    API.post(apiName, apiPath, apiData)
      .then((response) => {
        var url = response.url;
        console.log("url: ", url);
        //            var options = { headers: { "Content-Type": mediaType } };
        var file = new File([recording.blob], fileName);

        axios
          .put(url, file) //, options)
          .then(function (response) {
            console.log("axios put: ", response);
          })
          .catch(function (error) {
            console.log("axios put error", error);
          });
      })
      .catch((error) => {
        console.log(error);
      });
  }
},

The server code is:

body = json.loads(event['body'])
recordingInformation = body["data"]

try:
    s3Client = boto3.client('s3', config=Config(signature_version='s3v4'))
except Exception as e:
    print("exception is:", e)
    return {
        'statusCode': 400,
        'error': e
    }

bucket = os.environ['STORAGE_BUCKETRECORDINGS_BUCKETNAME']

fileName = recordingInformation['fileName']
expiryTime = datetime.now() + timedelta(minutes=3)
action = 'put_object'

try:
    URL = s3Client.generate_presigned_url(
        action, Params={"Bucket": bucket, "Key": fileName, "Expires": expiryTime, "ContentType": recordingInformation['fileType']})

    return {
        'statusCode': 200,
        'headers': {
            'Access-Control-Allow-Headers': '*',
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Methods': 'OPTIONS,POST,GET'
        },
        'body': json.dumps({'url': URL})
    }
except Exception as e:
    print("exception is: ", e)
    return {
        'statusCode': 400,
        'error': e
    }

The axios put gives the following error:

axios put error Error: Request failed with status code 403
    at createError (createError.js?770c:16:1)
    at settle (settle.js?8768:17:1)
    at XMLHttpRequest.onloadend (xhr.js?1a5c:54:1)

If I try the same URL via postman with file in Body/Binary, I get the following error:

<Code>SignatureDoesNotMatch</Code>
    <Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>

Any recommendations on what's off here?



Solution 1:[1]

After changing to generate_presigned_post, the client code looks like below:

        var signed_url = response.url;
        console.log("url: ", signed_url);

        const formData = new FormData();
        Object.keys(signed_url.fields).forEach((key) => {
          formData.append(key, signed_url.fields[key]);
        });
        // Actual file has to be appended last.

        //            var options = { headers: { "Content-Type": mediaType } };
        var file = new File([recording.blob], fileName);
        formData.append("file", file);

        axios
          .post(signed_url.url, formData) //, options)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 VV75