Cannot upload files with ACL public-read to Digital Ocean spaces
Asked Answered
M

5

7

I'm trying to upload images to a Digital Ocean space from the browser. These images should be public. I'm able to upload the images successfully.

However, though the ACL is set to public-read, the uploaded files are always private.

I know they're private because a) the dashboard says that the permissions are "private", and b) because the public urls don't work, and c) manually changing the permissions to "public" in the dashboard fixes everything.

Here's the overall process I'm using.

  1. Create a pre-signed URL on the backend
  2. Send that url to the browser
  3. Upload the image to that pre-signed url

Any ideas why the images aren't public?

Code

The following examples are written in TypeScript and use AWS's v3 SDK.

Backend

This generates the pre-signed url to upload a file.

import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'

const client = new S3Client({
    region: 'nyc3',
    endpoint: 'https://nyc3.digitaloceanspaces.com',
    credentials: {
        accessKeyId: process.env.DIGITAL_OCEAN_SPACES_KEY,
        secretAccessKey: process.env.DIGITAL_OCEAN_SPACES_SECRET,
    },
})

const command = new PutObjectCommand({
    ACL: 'public-read',
    Bucket: 'bucket-name',
    Key: fileName,
    ContentType: mime,
})

const url = await getSignedUrl(client, command)

The pre-signed url is then sent to the browser.

Frontend

This is the code on the client to actually upload the file to Digital Ocean. file is a File object.

const uploadResponse = await fetch(url, {
    headers: {
        'Content-Type': file.type,
        'Cache-Control': 'public,max-age=31536000,immutable',
    },
    body: file,
    method: 'PUT',
})

Metadata

  • AWS SDK: 3.8.0
Montcalm answered 9/3, 2021 at 22:51 Comment(0)
M
16

Turns out that for Digital Ocean, you also need to set the public-read ACL as a header in the put request.

//front-end
const uploadResponse = await fetch(url, {
    headers: {
        'Content-Type': file.type,
        'Cache-Control': 'public,max-age=31536000,immutable',
        'x-amz-acl': 'public-read', // add this line
    },
    body: file,
    method: 'PUT',
})
Montcalm answered 10/3, 2021 at 4:56 Comment(6)
Life saver. I have not seen this in any of the Digital Ocean docs. Thanks.Recital
Thanks, was struggling with that for a bit.Accomplishment
And don't forget to add the x-amz-acl header to the allowed origins :) Settings > CORS Configurations > Add or Edit > Allowed HeadersMission
I have been using the presigned_post_url and tried it but its not working in that, can anyone help me in understanding how to do so?Barton
Thanks a lot! I spent so much time trying to figure this out.Eduard
same here, thanks for the post, I also spent a lot of time until i see this post!Kazak
H
1

I don't have the reputation to comment, hence adding a response. Thank you @Nick ... this is one of the few working examples of code I have seen for DigitalOcean pre-signed url. While the official DigitalOcean description here mentions Content-Type is needed for uploading with pre-signed urls, there is no example code.

Another mistake that prevented me from uploading a file using pre-signed URLs in DigitalOcean was using 'Content-Type':'multipart/form-data' and FormData().

After seeing this post, I followed @Nick's suggestion of using a File() object and 'Content-Type':'<relevant_mime>'. Then, the file upload worked like a charm. This is also not covered in official docs.

Hippie answered 12/10, 2021 at 17:22 Comment(0)
M
1

don't have enough reputation to comment but thanks so much for the help @nick. That worked like a charm. I added

 ContentType: ' Headers: {
    'Content-Type': file.type,
    'Cache-Control': 'public,max-age=31536000,immutable',
    'x-amz-acl': 'public-read', // add this line
  },
  ContentType: '<relevant_mime>',',

and it worked perfectly <3

Macrocosm answered 8/2 at 21:29 Comment(0)
L
0

Try this to force ACL to Public in Digital Ocean Spaces:

s3cmd --access_key=YOUR_ACCESS_KEY --secret_key=YOUR_SECRET_KEY --host=YOUR_BUCKET_REGION.digitaloceanspaces.com --host-bucket=YOUR_BUCKET_NAME.YOUR_BUCKET_REGION.digitaloceanspaces.com --region=YOUR_BUCKET_REGION setacl s3://YOUR_BUCKET_NAME --acl-public
Laryngo answered 13/3, 2021 at 15:17 Comment(0)
B
0

For those using pre-sing url and then POST upload with public access for files I had to add:

{"acl": "public-read" }

to policy document.

So whole Policy document for generating pre-sing url looks like that:

{
  "expiration": "2009-01-01T12:00:00.000Z",
  "conditions": [
    {"bucket": "<bucketname>" },
    {"acl": "public-read" },
    ["eq", "$key", "testfile.txt"],
    ["starts-with", "$Content-Type", "text/"],
  ]
}

You can also try this playground for S3 POST upload

Bitterroot answered 13/8 at 21:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.