a blog for those who code

Sunday, 3 March 2019

Upload Images to Digital Ocean Spaces using React

In this post we will be discussing about uploading images to Digital Ocean Spaces using React.js. Digital Ocean Spaces allows you to store and vast amount of contents be it images, video or audio. It's S3 compatible object storage which has built in CDN that makes scaling easy, reliable and affordable. The pricing starts from $5 in which you will get 250 GB of storage, 1 TB of outbound transfer and unlimited uploads.

We will be using DO Spaces API, which provides RESTful XML API for pro-grammatically managing the data. The API is inter-operable with Amazon's AWS S3 API that means it allows you to interact with the service while using the tools you already know.

Prerequistes


To work with this tutorial you need to have a DigitalOcean Space, along with an access key and secret key to your account. Learn here how to set up the Digital Ocean Space along with access key and secret key. Also you need to install aws-sdk in you application using npm : npm install --save aws-sdk


Code


At first we will be creating a file named DigitalOcean.js and add the following code to it

import AWS from 'aws-sdk';

/**
 * Digital Ocean Spaces Connection
 */

const spacesEndpoint = new AWS.Endpoint('nyc3.digitaloceanspaces.com');
const s3 = new AWS.S3({
      endpoint: spacesEndpoint,
      accessKeyId: process.env.ACCESS_KEY_ID,
      secretAccessKey: process.env.ACCESS_SECRET_KEY
    });
export default s3;


In the above code we are connecting to the DigitalOcean Spaces API. At first you need to see which region your spaces is located and the spacesEndPoint will point to that location, in our case it is nyc3. You need to add the ACCESS_KEY_ID and ACCESS_SECRET_KEY in the environment variable.

We will also be creating a config.js file which will have the bucket name and DO Spaces full url as shown below :

export default {
  digitalOceanSpaces: 'https://myBucketName.nyc3.digitaloceanspaces.com/',
  bucketName: 'myBucketName'
};

On the front-end we will be adding a input tag of type file which will always accept an image type as shown below :

<input type="file" id="inputfile" accept="image/*"
            onChange={this.handleImageChange} />

Then we will have a prop which will send the data to the Digital Ocean Spaces using the S3.putObject function as shown below :

handleImageChange = (e) => {
  if (e.target.files && e.target.files[0]) {
    const blob = e.target.files[0];
    const params = { Body: blob, 
                     Bucket: `${Config.bucketName}`, 
                     Key: blob.name};
     // Sending the file to the Spaces
     S3.putObject(params)
       .on('build', request => {
         request.httpRequest.headers.Host = `${Config.digitalOceanSpaces}`;
         request.httpRequest.headers['Content-Length'] = blob.size;
         request.httpRequest.headers['Content-Type'] = blob.type;
         request.httpRequest.headers['x-amz-acl'] = 'public-read';
      })
      .send((err) => {
        if (err) errorCallback();
        else {
        // If there is no error updating the editor with the imageUrl
        const imageUrl = `${Config.digitalOceanSpaces}` + blob.name
        callback(imageUrl, blob.name)
       }
    });
  }
};

Do note that x-amz-acl is only needed if you want to allow the public to have an access to your images otherwise you can remove that line.

15 comments:

  1. There is a typo in code. add "const" before blob.

    ReplyDelete
  2. how to with react native, i have react native app and want to upload images direct from my react native app to Spaces .. will this work or need something else ???

    ReplyDelete
  3. After uploading, how can I retrieve the url of the image stored in digital ocean spaces using GET and show that on the front end of my app?

    ReplyDelete
    Replies
    1. This is the image url stored in digital ocean spaces
      const imageUrl = `${Config.digitalOceanSpaces}` + blob.name

      Delete
  4. Hey man, I'm learning how to upload files to DO and use them as profile images. However, I already have a component where I'm showing the upload section to edit the profile photo. e.g.: "/client/src/profile/profile.js"

    In that case, where should I upload the config.js and digitalocean.js files? And how do I connect them to my component?

    I'm utterly confused because most DigitalOcean tutorials don't explain this. I followed this one: https://www.digitalocean.com/community/tutorials/how-to-upload-a-file-to-object-storage-with-node-js#create-the-front-end-of-the-application - but I got stuck at making it work with a component within the app.

    ReplyDelete
  5. Hey man! Thanks for this tutorial. It's a little different from the official DO: https://www.digitalocean.com/community/tutorials/how-to-upload-a-file-to-object-storage-with-node-js#create-the-front-end-of-the-application

    Anyway, I started following the one above, but I couldn't find a way to make this upload-image component work. Why? Because I already have an app working with the component file not in the index.html.

    Let's say the component where I want to add the upload function is in "client/src/profile/profile-form" - where should I put the config.js and the digitalocean.js files? And how do I use them (with a standard import function?)?

    ReplyDelete
  6. Hi There, it worked on my Mobile App but now i am working on React/Next.js web app but CORS missing allow origin error .. any setting from DigitalOcean spaces side ??? currently my domain name is localhost as its in development

    ReplyDelete
    Replies
    1. You can look at https://www.digitalocean.com/community/questions/why-can-i-use-http-localhost-port-with-cors-in-spaces to handle CORS issue in localhost

      Delete
  7. Hi, first of all thanks for the post, it's really helpful but, is this secure? The Secret gets exposed in the front, right?

    ReplyDelete
    Replies
    1. Yes its secure, the secret does not expose in the frontend. You can make it more secure by encrypting the secret or loading it from some file from your file system.

      Delete