DEV Community


Posted on


FIXED: Upload to DigitalOcean Spaces with AWS S3 getSignedUrl with correct permissions and Content Type


What i actually had to do, was allow for custom headers to be set in the put request. This was done through the DigitalOcean interface in the CORS settings for your spaces.

I am trying to upload assets through the getSignedUrl method that the aws-sdk provides, a NodeJS backend with Axios where the upload happens from a VueJS 2 frontend. Now whatever combination i try, or the file is able to upload but always sets it to private and the wrong Content-Type, or i get 403 permission errors.

If i leave out all the ACL and ContentType stuff, the file is displayed on the Spaces interface as being private.

This is how it should be shown, and what i get when i upload manually through the interface:

Alt Text

What i want to do is that the upload has an ACL that is public-read and the correct Content-Type.

The following is the code that i use:

To get a signed Url

const s3 = new AWS.S3({
    accessKeyId: keys.spacesAccessKeyId,
    secretAccessKey: keys.spacesSecretAccessKey,
    signatureVersion: 'v4',
    endpoint: '',
    region: 'REGIO'

app.get('/api/upload/image', (req, res) => {
        const type = req.query.type;

        s3.getSignedUrl('putObject', {
            Bucket: 'BUCKET',
            ContentType: type,
            ACL: 'public-read',
            Key: 'random-key'
        }, (error, url) => {
            if (error) {
            console.log('KEY:', key);
            console.log('URL:', url);
            res.send({ key, url });
Enter fullscreen mode Exit fullscreen mode

This provides a signed url that looks like:
Enter fullscreen mode Exit fullscreen mode

Then for actually uploading the file i use the following:

await UploadService.uploadImage(, file, {
          'x-amz-acl': 'public-read',
          'Content-Type': file.type
Enter fullscreen mode Exit fullscreen mode

With the service looking like:

  async uploadImage (url, file, headers) {
    await axios.put(url, file, headers.headers);
Enter fullscreen mode Exit fullscreen mode

disclaimer: this works with S3 itself as you can set the permissions on a per bucket level so you don't have to worry about it. But Spaces providers a CDN as well and is just generally cheaper. :)

Top comments (4)

nickfoden profile image
Nick Foden

LIFESAVER! Thank you thank you for posting this! Was going round and round with different ways to upload and losing my mind. Didn't realized had to put the ACL public headers both when generating url and also when uploading.

Working well for me now when deployed.

Now if I could just figure out how to get around the Digital Ocean Cors config when on localhost . .

noitidart profile image

Were you able to ever get this working on localhost?

ekonurarifin85 profile image
Eko Nur Arifin

my side ald try all but can fix just need this on setting space
Image description

yasir5247 profile image

I couldn't get it working with following code. :(

An Animated Guide to Node.js Event Loop

Node.js doesn’t stop from running other operations because of Libuv, a C++ library responsible for the event loop and asynchronously handling tasks such as network requests, DNS resolution, file system operations, data encryption, etc.

What happens under the hood when Node.js works on tasks such as database queries? We will explore it by following this piece of code step by step.