How can you upload an image in AWS S3 bucket directly with presigned URL
Tags: aws, serverless, node, react
Published: 2024-02-27 21:13:41

# Why you should use this method?

Firstly, with the presigned URL, you don't have to send your file to the backend to upload it to the S3 bucket; you can directly upload the file from your front-end application to the S3 bucket securely. It's a convenient and secure approach. This URL can be provided to your users to grant temporary access to a specific S3 object.

## How to use the presigned URL

1. **Create an S3 bucket**

Here, I am creating an S3 bucket through a CloudFormation template in YAML with minimal configuration. You can also create it from the AWS console or convert it to JSON.

```yaml
resources:
  Resources:
    MyS3Bucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: My-S3-Bucket-Name
        VersioningConfiguration:
          Status: Enabled
        CorsConfiguration:
          CorsRules:
            - AllowedMethods:
                - GET
                - PUT
                - POST
                - HEAD
              AllowedOrigins:
                - "*"
              AllowedHeaders:
                - "*"

    MyS3BucketPolicy: 
      Type: 'AWS::S3::BucketPolicy'
      Properties:
        Bucket: !Ref MyS3Bucket
        PolicyDocument:
          Statement:
            - Action:
                - 's3:GetObject'
              Effect: Allow
              Resource: !Join
                - ''
                - - 'arn:aws:s3:::'
                  - !Ref MyS3Bucket
                  - /*
              Sid: "PublicReadGetObject"
              Principal: '*'
```

2. **Make a Lambda function to get a signed URL**

```javascript
const AWS = require("aws-sdk");
const region = process.env.REGION;
AWS.config.update({ region });
const s3 = new AWS.S3();

async function getPresignedUploadUrl(event) {
  const { bucket, directory, fileName } = event;

  const key = `${directory}/${fileName}`;

  const url = await s3.getSignedUrl("putObject", {
    Bucket: bucket,
    Key: key,
    ContentType: "image/*",
    Expires: 300
  });
  return url;
}

module.exports = { getPresignedUploadUrl };
```

3. **Function to get the signed URL in the frontend**

```javascript
const getS3Signeture = async (name) => {
  // name = name of the current selected file EG: image.jpg
  const result = await axios.post("ENDPOINT-OF-THE-getPresignedUploadUrl-LAMBDA", {
    bucket: "My-S3-Bucket-Name",
    directory: "FOLDER-NAME",
    fileName: Date.now() + name,
  });
  return result.data;
};

export default getS3Signeture;
```

4. **Function to upload the file to S3 with the presigned URL**

```javascript
const s3UploadImage = async (presignedUploadUrl, file) => {
  const response = await fetch(
    new Request(presignedUploadUrl, {
      method: "PUT",
      body: file,
      headers: new Headers({
        "Content-Type": "image/*",
        ACL: "public-read",
      }),
    })
  );

  if (response.status === 200) {
    const data = {
      url: response.url.split("?")[0],
      status: 200,
    };
    return data;
  } else {
    const failed = {
      url: "",
      status: 500,
    };

    return failed;
  }
};

export default s3UploadImage;
```

Please note that you may need to adapt the code to your specific use case and environment. Additionally, ensure you have the necessary dependencies such as Axios for making HTTP requests.