webflux s3 upload 삽질일기

RACOONMAN98·2023년 1월 20일
0

삽질일기

목록 보기
5/5
post-thumbnail

What is this work? 🔨

  • " /upload " 에 multipart/form-data 타입의 요청이 오면 해당 이미지를 aws s3 버켓에 업로드하는 기능을 구현하였습니다.
  • erd테이블과의 원활한 동기화를 위한 entity 필드들, sql 쿼리의 수정이 있었습니다.
  • build.gradle 에 주석으로 의존성을 정리했습니다

point 📌

  • S3 파일 업로드 방식을 MVC 패턴에서 했었던 일반적인 방식으로 구현하려 했으나 방식이 상이하여 어려움을 겪었습니다.
  • 한 번의 요청에 최대 5mb 크기의 파일만 가능하게 설정하였으나 ,
  • 무차별 파일 업로드 공격에 대한 S3 요금이 과금되지 않도록 지갑을 지키려면
  • 업로드 요청간 텀을 둔다던지, 게시글 하나당 사진의 갯수나, 하나의 게시글의 총 파일의 용량을 제한하는 방식으로 외부에서의 공격에 대해 대비해야 할 것같습니다.
  • 이와 관련된 좋은 방법이 있다면 공유 부탁드립니다.

Background 🖼️

  • AWS 에 종속적인 기능인탓인지 에러에 관해 구글링을 해보아도 에러에 관련된 자료가 적어서 난항을 겪었습니다.
  • 결과적으론 원래 만났던 에러를 해결하지 못하고 구글신의 힘을 빌려 구현하였습니다.

important ❓

  • 제가 마주했던 에러상황을 공유해보겠습니다.
  • 먼저 스프링 MVC 에서 했던 방식처럼 진행을 했었습니다.
  • MediaType 도 지정해주었고 @requestPart 방식으로 MultipartFile을 받는 정석적인(?) 방법인데요 .
    image
  • 요청을 보내보니..
    image

image

  • image/jpeg,image/jpg, 를 지원하지 않는다는 에러가 떴습니다 . png, webp도 마찬가지였습니다.

  • 제가 보내는 타입마다 그 타입은 지원하지 않는다 라는 무슨 초 적응 생명체가 되어버린 무적의 에러가 뜨는 것 입니다..

  • 그래서 이 시점에서 구글링을 열심히 해본결과 Spring webflux에선 MultipartFile.class 를 사용하지 않고 Filepart 형식으로 multipart/form-data 를 처리한다는 사실을 알아내게 되었습니다.

-따라서 구현을 해보았는데요 .
image

  • StoreImage 의 내부입니다. s3 업로드를 요청한 파일에 대한 vaildate와 UUID로 이름을 변경하는 로직을 거쳐서 이 로직으로 도착하는데요
  • image

new PutObjectRequest() 가 파일 업로드를 담당하는 객체입니다. 매개변수로 ,s3버켓의 이름, 파일이름, 파일의 inputstream, 파일의 metadata값을 받습니다.

  • 저같은경우는 metadata 설정을 위해서 new objectmetadata 객체를 생성해서
    image

  • 업로드요청을 한 FilePart 안에있는 ContentType과 ContentLength 를 꺼내서 metadata를 설정해서 넘겨주면 되겠다!
    라고 판단하고 업로드 요청을 보내봤습니다만.

reactor.core.Exceptions$ErrorCallbackNotImplemented: com.amazonaws.services.s3.model.AmazonS3Exception: The request signature we calculated does not match the signature you provided. Check your key and signing method. (Service: Amazon S3; Status Code: 403; Error Code: SignatureDoesNotMatch; Request ID: YRCSMZ7Q18TF3H28; S3 Extended Request ID: ha6p1admq3OnBTaIKSnrCgBdwZVKMFVueH3Fd8JvxVMlrkMGGeTh+4l/i1QcG424Ik42gqPzm+QFc9zYZZn6Qw==; Proxy: null), S3 Extended Request ID: ha6p1admq3OnBTaIKSnrCgBdwZVKMFVueH3Fd8JvxVMlrkMGGeTh+4l/i1QcG424Ik42gqPzm+QFc9zYZZn6Qw==
Caused by: com.amazonaws.services.s3.model.AmazonS3Exception: The request signature we calculated does not match the signature you provided. Check your key and signing method. (Service: Amazon S3; Status Code: 403; Error Code: SignatureDoesNotMatch; Request ID: YRCSMZ7Q18TF3H28; S3 Extended Request ID: ha6p1admq3OnBTaIKSnrCgBdwZVKMFVueH3Fd8JvxVMlrkMGGeTh+4l/i1QcG424Ik42gqPzm+QFc9zYZZn6Qw==; Proxy: null)

【The request signature we calculated does not match the signature you provided .】 라는 에러를 만났고 대체 달라는대로 줬더니 왜 안맞는다는거냐. 라고 생각해서 구글링을 해본결과 해결한 사람들은 accessKey나 SecretKey값이 달랐다느니, SecretKey에 특수문자가 껴있으면 안되니까 없을때까지 재생성해보라느니 (무슨 민간요법도아니고) 그래서 저도 IAM 계정을 다섯번 정도 생성해서 특수문자가 없는 시크릿키 계정으로 시도해도 똑같았습니다
ㅋㅋ그럼그렇지

그렇게 스택오버플로우를 헤집고다니며 디지털 노마드가 되어가는 와중에 content-length가 요청과 일치하지 않을 때 발생하는 에러일수도있다는 댓글을 보고 FilePart에서 ContentLength를 log를 찍어봤습니다.
그랬더니 ...??
image

-1 값이 떴는데요 전송이 끝나기전까지 length를 모르기 때문에 뜨는 값이라고 나왔습니다.

그렇기에 -1을 메타데이터에 넣는게아니라 , '그러면 요청 헤더에서 content-length 뽑아와서 집어넣어 줘볼까? " 라는 생각으로
@ReuqestHeader 에서 cotentLength를 뽑아서 메타데이터에 넣었더니

image

  • dataLength는 1024 (아마도 해당요청이 1회당 퍼올릴수있는 Byte[] 의 최대 크기라고 생각함 ) expectedLength는 요청헤더에서 뽑아준 크기만큼 나왔습니다..

  • 화가 난 저는 너 알아서 해보라고 메타데이터에 contentLength를 지정안해주고 요청을 보내보기도 해봤는데요

image

이 친구가 메모리 없다고 저보다 화가 더 많이 났더라구요 .
( contentLength를 metadata 에 지정을 안해주었을 경우 뜨는 에러입니다.. )
(중간에 FileFile의 내용을 inputstream을 bufferdImage로 변환하고 bufferdImage를 File 로 생성해서 전송하는 방법도 시도해보았는데요. 마찬가지 였습니다..)

  • 지금와서 생각해보니 제가 들어오는 inputstream을 차곡차곡 담고 담고 담은 inpustream 완전체를 S3에 보내줬어야 하지않았나~ 라는 생각이 듭니다. 하지만 구현 해보라고 한다면 못할 것 같습니다.. 구현 실력을 좀 더 키운다음에 도전해 보아야겠습니다.

  • 혹시 구현방법을 알고계신분은 가르쳐주시면 감사하겠습니다. 어떻게 구글링 해야할지 모르겠어서 밑의 깃허브 방법을 시도했습니다..

https://github.com/anicetkeric/aws-s3-spring-webflux/blob/10be960ae6/src/main/java/com/bootlabs/awss3springwebflux/common/FileUtils.java

감사합니다...

결국엔 AWS S3 에서 지원하는 s3 multipart upload API 로 구현에 성공했습니다..
하루종일 뭐한거지?
image

  • 한계점으로는 아직 이 s3 multipart upload API 의 프로세스를 잘 모른다는 점이 있습니다..
profile
공부일기

0개의 댓글