정신을 잃지 않고 S3에 업로드

TL;DR 실제 코드 헤더로 스크롤

이야기



당신이 나와 같고 미워한다면 사용자 자산으로 작업하고 축하합니다. 당신은 제정신인 사람입니다!

특히 S3에 대한 업로드를 처리하는 안전하고 간결하며 지능적이며 깨끗한(또는 slic (방금 약어를 만들었습니다)) 방법을 찾기 위해 며칠을 보냈습니다.

이제 저는 많은 방법이 있다는 것을 압니다. 나는 그들 모두를 들었다. 나는 100개의 기사와 대부분의 aws 문서를 샅샅이 뒤졌습니다.
내가 멍청하거나 이전 방식에 대한 존경심이 부족할 수 있습니다. 제공되는 솔루션에는 multer multiparty 과 같은 해킹 도구가 앱에 있습니다. 실제로 nextjs 애플리케이션처럼 지원하지 않더라도 . 또는 1999년처럼 어둠의 마법에서 가져온 형태로 숨겨진 요소를 생성하거나 최악의 경우 파일을 읽을 수 있는 증기로 변환한 다음 바이트 배열로 변환하고 코드를 읽는 다음 사람이 지루하여 암기하기를 기도합니다. 파일 관련 API 또는 거대한 js 괴짜입니다.

이제 파일 업로드와 파일 업로드를 구분해 보겠습니다.

멋진 방식으로 File 내부에 <input />를 가져오는 것이 아닙니다. 이를 위해 uppy react-dropzone 과 같은 놀라운 솔루션이 많이 있으므로 직접 만든 솔루션을 만드는 것보다 사용하는 것이 좋습니다.

우리는 당신이 그것을 가지고 있으면 망할 파일로 무엇을 할 것인지에 대해 이야기하고 있습니다. 갤러리에 관해서는 내가 당신의 구세주가 아니기 때문에 단일 파일을 업로드하는 더 간단한 사용 사례를 살펴보겠습니다. 죄송합니다 🤷‍♂️

그래서 문제를 이해합시다. 자산을 S3에 직접 업로드할 수 없는 이유는 무엇입니까? 기술적으로 가능하며 수행 방법 및 모든 종류의 깔끔한 내용을 문서화합니다! 내가 당신의 자격 증명을 훔치고 내가 원하는 것을 업로드해도 상관없다면 계속하세요! 노출 위험이 있습니다.

일반적으로 당신은 쓴 약을 삼키고 누군가 어딘가에 동기가 부여되어 너무 오래되거나 심지어 망가지지 않도록 최근에 건전한 해결책을 작성하도록 기도할 것입니다.

하지만 우리는 그렇게 하고 싶지 않습니다.
그렇다면 서버를 거치지 않고 파일을 안전하게 가져오려면 어떻게 해야 할까요? 간단합니다. S3 버킷으로 직접 이동할 수 있는 매우 안전한 임시 포털을 만듭니다!

어떻게? 음, 주위를 기웃거리면 때때로 BC 53년경에 aws-sdk 패키지에 S3 메서드가 있는 createPresignedPost 모듈이 있음을 알 수 있습니다. 그 방법이 뭡니까? 할당된 시간에 함께 사용하면 서버를 거치지 않고 자산을 업로드하는 데 사용할 수 있는 URL 및 필드 콤보를 생성합니다. 우와! 😃
안타깝게도 더 이상 가지고 있지 않습니다. 🤦‍♂️

아니면 우리가? 🤔
조금 더 스누핑하면 aws-sdk가 클라이언트로 분할된다는 것을 알 수 있습니다. 좋습니다. @aws-sdk/client-s3 . 더 자세히 보면 @aws-sdk/s3-presigned-post 이 있습니다. 좋아, 정상으로 돌아왔어! 🥳

이것은 충분한 이야기입니다. 나는 이것에 너무 많은 시간을 낭비했고 나 자신과 나의 여정을 표현하고 싶었습니다.

실제 코드



먼저 서버를 생성해야 합니다. 이미 서버가 있다고 가정해 보겠습니다. 필요한 URL과 필드를 반환하는 엔드포인트가 있어야 합니다.
필요한 패키지를 추가합니다.

$ yarn add @aws-sdk/client-s3 @aws-sdk/s3-presigned-post


원하는 대로 aws 자격 증명을 제공하십시오. 환경 변수를 사용하겠습니다.

AWS_ACCESS_KEY_ID=<ACCESS_KEY_ID>
AWS_SECRET_ACCESS_KEY=<SECRET_ACCESS_KEY>


끝점을 만들고

// controllers/createUploadUrl.ts

import { createPresignedPost } from "@aws-sdk/s3-presigned-post"
import { S3Client } from "@aws-sdk/client-s3"

// fill your region and anything else you care about
const s3Client = new S3Client({ region: 'eu-central-1' })

// idk what framework you're using, adapt!
const handler = async (req: Request, res: Response) => {
  const { url, fields } = await createPresignedPost(s3Client, {
    Bucket: 'my-own-unique-bucket',
    Key: `my-app/assets/${req.body['fileName']}`,
    Fields: { acl: 'public-read' },
    Conditions: [{ acl: "public-read" }, { bucket: "my-own-unique-bucket" }, ["starts-with", "$key", "my-app/assets/"]

 return { url, fields }
}


특히 Conditions 필드에 유의하십시오. 해당 필드는 URL과 필드를 프런트엔드로 다시 반환한 후 요청에 놀라운 변경 사항이 적용되지 않도록 합니다. 숨겨진 자격 증명을 사용하지 않고 자폭 타이머와 함께 추가하면 클라이언트 파일이 날아갈 수 있는 매우 안전한 포털을 갖게 됩니다!

이제 프런트엔드로 돌아가서


async function sendAsset(file: File) {
  // use fetch or axios or whatever to reach your endpoint
  const { url, fields } = await createUploadUrl({
    name: `logo.${file.name.split(".").pop()}`,
  })
  // FormData accepts only a form element in the constructor, so we gotta build it ourselves
  const formData = new FormData()
  Object.entries(fields).forEach(([field, value]) => {
    formData.append(field, value)
  })
  // now the important part!
  formData.append('file', file) // Note, it has. to be called file!

  // This is also very important! Undocumented too 😢
  const headers = new Headers({'Content-Length': `${file.size + 5000}`})

  await fetch(url, {
    method: "POST",
    headers,
    body: formData
  })
}


바다빙바다붐. 주위에 해킹이 없습니다.
나는 이것이 누군가를 돕기를 바랍니다. 나는 이것으로 너무 오랫동안 어려움을 겪었습니다.

자산 업로드를 어떻게 처리하는지 댓글로 알려주세요 🥰

좋은 웹페이지 즐겨찾기