POST uploads to S3 bucket failing with http 405

0

Problem

I'm trying to upload files to an S3 bucket using presigned post requests (roughly following this blog post). My server (node) generates a presigned post request using createPresignedPost, and then returns it to the client (React), which uses it to upload a user-generated file to our S3 bucket.

Currently, any POST requests from the client to the bucket all fail with 405 Method Not Allowed. I checked the response headers, and saw that the Allow header does not list POST, although the Access-Control-Allow-Methods does.

Question: is there something else I need to do in order to allow POST requests to the bucket?

note: the reason I'm trying to used presigned POST requests as opposed to the regular presigned urls (which use PUT), is because I need to specify restrictions on file type and size, which the latter doesn't seem to support - if i'm mistaken about this, and anyone knows a way to specify restrictions through presigned PUT requests, then this would also solve my problem!

Context / other info

The S3 bucket has no policy. It does have a CORS policy, which is

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "POST",
            "PUT",
            "HEAD",
            "DELETE"
        ],
        "AllowedOrigins": [
            "http://localhost:3000",
            "http://localhost:8000",
            ...
        ],
        "ExposeHeaders": [
            "ETag",
            "x-amz-meta-custom-header"
        ]
    }
]

The server-side code for generating the presigned POST request:

const signS3PostRequest = async ({ fileName, fileType, prefix, bucket }) => {
  const params = {
    Bucket: bucket,
    Fields: {
      $key,
    },
    Expires: 60,
    ContentType: fileType,
    ACL: 'private',
    Conditions: [['content-length-range', 0, 10000000]],
  }

  const s3 = new aws.S3({
    apiVersion: 'latest',
    region: $region,
    accessKeyId: process.env.AWS_ACCESSKEYID,
    secretAccessKey: process.env.AWS_SECRETACCESSKEY,
  })

  return new Promise((resolve, reject) => {
    s3.createPresignedPost(params, (err, data) => {
      if (err) {
        ...
      } else {
        resolve(data)
      }
    })
  })
}

And the client-side code for uploading a file using this presigned request:

export const uploadFileToS3UsingSignedPostRequest = (
  file,
  signedRequestData
) => {
  let form = new FormData()
  Object.keys(signedRequestData.fields).forEach((k) =>
    form.append(k, signedRequestData.fields[k])
  )
  form.append('file', file)
  return fetch(signedRequestData.url, { method: 'POST', body: form })
}

The response I get back from s3.createPresignedPost looks like this:

{
	"url": "https://s3.amazonaws.com/$BUCKET_NAME",
	"fields": {
		"key": "test/6zb3rewm8cjydpslti3g/test.jpeg",
		"bucket": "$BUCKET_NAME",
		"X-Amz-Algorithm": "AWS4-HMAC-SHA256",
		"X-Amz-Credential": "$CREDENTIAL",
		"X-Amz-Date": "20221230T090441Z",
		"Policy": "eyJleHBpcmF0aW9uIjoiMjAyMi0xMi0zMFQwOTowNTo0MVoiLCJjb25kaXRpb25zIjp...",
		"X-Amz-Signature": "$SIGNATURE"
	}
}

and the response from S3 when trying to use the signed post request to upload the file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>MethodNotAllowed</Code>
    <Message>The specified method is not allowed against this resource.</Message>
    <Method>POST</Method>
    <ResourceType>OBJECT</ResourceType>
    <RequestId>HTYWNACA6A1J6BBS</RequestId>
 <HostId>BSV4mFijEfgZEiMu6nVxqP9aocuhAwN7et/UfrO4r1PmkeFZ8nVUsNmcLVZoWCe06zrEFJXDfsE=</HostId>
</Error>
gefragt vor einem Jahr3484 Aufrufe
1 Antwort
0
AWS
vtjean
beantwortet vor einem Jahr

Du bist nicht angemeldet. Anmelden um eine Antwort zu veröffentlichen.

Eine gute Antwort beantwortet die Frage klar, gibt konstruktives Feedback und fördert die berufliche Weiterentwicklung des Fragenstellers.

Richtlinien für die Beantwortung von Fragen