DEV Community

Cover image for Push that to AWS S3 for me
Phan Công Thắng
Phan Công Thắng

Posted on • Originally published at thangphan.xyz

Push that to AWS S3 for me

Hi friend! Today, I'm going to introduce to you how I upload the image to AWS S3 using Javascript.

Let's dig in!

Core Concept

Grap a drink and take a glance with about the core concept of AWS S3. I'm try to keep it simple as much as possible.

Here we go:

Bucket

Bucket is where contains the objects we would like to upload, and it is unique global.

Object

The object might be an image, a font file, or whatever file we want to upload to Bucket. The object has a unique key, and metadata like the type of image, type of file.

CORS

By default, AWS S3 will block public access, and our object will be private. So we need to set up cors for our object in order to public it.

Flow

My goal is to allow the user can upload the object to the bucket that I defined.In AWS S3, we can use presigned URL, in order to allow users to do this..

First, We use the bucket's name, AWS access key, AWS secret key to get the signed URL. Second, we use the signed URL to allow users to make the request to our bucket like: putObject, deleteObject...

Here is the flow: My Flow

Coding

Setup

Let's create a nextjs app quickly:

npx create-next-app@latest --typescript
Enter fullscreen mode Exit fullscreen mode

Next, add a bucket on AWS S3:

I call olala-bucket.

My bucket

You need to create an AWS access key and AWS secret key.

Write code

I install aws-sdk to make a connect to AWS s3.

First of all, I need to create an API nextjs that creates the signed URL. It receives the key of the object, fileType from the user's request.

import type {NextApiRequest, NextApiResponse} from 'next'
import {responseError} from '@/utils/api-stuff'
import aws from 'aws-sdk'

// you need to set up these values in .env file

const s3 = new aws.S3({
  accessKeyId: process.env.AWS_ACCESS_KEY,
  secretAccessKey: process.env.AWS_SECRET_KEY,
})

function responseError(res: NextApiResponse, message: string) {
  return res.status(404).json({message})
}

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse,
) {
  const {key, fileType} = req.query

  if (!key) {
    return responseError(res, 'Key of object is required!')
  }

  if (!fileType) {
    return responseError(res, 'MetaData of object is required!')
  }

  const bucketParams = {
    Bucket: process.env.BUCKET_NAME,
    Key: key,
    ContentType: fileType,
  }

  try {
    const signedUrl = await s3.getSignedUrl('putObject', bucketParams)

    res.status(200).json({url: signedUrl})
  } catch (error) {
    res.status(401).json({message: (error as Error).message})
  }
}
Enter fullscreen mode Exit fullscreen mode

Make a request to receive signed url, and save it to a value.

const signedUrlRef = React.useRef<string | null>(null)

React.useEffect(() => {
  const query = new URLSearchParams({
    // name of object
    key: 'naruto',
    fileType: 'image/jpeg',
  })

  async function getSignedUrls() {
    try {
      const {url} = await fetcher(`/api/your-bucket?${query}`)
      if (!signedUrlRef.current) {
        signedUrlRef.current = url
      }
    } catch (error) {
      console.log('GetSignedUrls._error:', error)
    }
  }

  getSignedUrls()
}, [])
Enter fullscreen mode Exit fullscreen mode

And add a form element to submit our image:

async function handleFormSubmit(event: React.FormEvent<HTMLFormElement>) {
  event.preventDefault()
  if (!signedUrlRef.current) return

  const {myFile} = event.currentTarget
  const file = myFile.files[0]

  try {
    await fetch(signedUrlRef.current, {
      method: 'PUT',
      headers: {
        'Content-Type': file.type,
      },
      body: file,
    })
  } catch (error) {
    console.log('pushObjectToBucket._error:', error)
  }
}
return (
  <div>
    <form onSubmit={handleFormSubmit}>
      <input type="file" name="myFile" />
      <button>Submit</button>
    </form>
  </div>
)
Enter fullscreen mode Exit fullscreen mode

This is all of code:

import * as React from 'react'

const fetcher = (args: string) => fetch(args).then((res) => res.json())

function Demo() {
  const signedUrlRef = React.useRef<string | null>(null)

  React.useEffect(() => {
    const query = new URLSearchParams({
      // name of object
      key: 'naruto',
      fileType: 'image/jpeg',
    })

    async function getSignedUrls() {
      try {
        const {url} = await fetcher(`/api/your-bucket?${query}`)
        if (!signedUrlRef.current) {
          signedUrlRef.current = url
        }
      } catch (error) {
        console.log('GetSignedUrls._error:', error)
      }
    }

    getSignedUrls()
  }, [])

  async function handleFormSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault()
    if (!signedUrlRef.current) return

    const {myFile} = event.currentTarget
    const file = myFile.files[0]

    try {
      await fetch(signedUrlRef.current, {
        method: 'PUT',
        headers: {
          'Content-Type': file.type,
        },
        body: file,
      })
    } catch (error) {
      console.log('pushObjectToBucket._error:', error)
    }
  }
  return (
    <div>
      <form onSubmit={handleFormSubmit}>
        <input type="file" name="myFile" />
        <button>Submit</button>
      </form>
    </div>
  )
}

export default Demo
Enter fullscreen mode Exit fullscreen mode

Let's now hit the submit button and check our bucket:

Upload file to AWS S3 successfully

Conclusion

Here is the way I upload the image to AWS S3 using nextjs framework. Why don't try to upload your image or whatever file you would like to add to the bucket.

Discussion (0)