1- //package popcong.app.adapter.out.image;
2- //import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
3- //import com.amazonaws.regions.Regions;
4- //import com.amazonaws.services.s3.AmazonS3;
5- //import com.amazonaws.services.s3.AmazonS3ClientBuilder;
6- ////import com.amazonaws.services.s3.model.ObjectMetadata;
7- ////import com.amazonaws.services.s3.model.PutObjectRequest;
8- //import lombok.extern.slf4j.Slf4j;
9- //import org.springframework.stereotype.Component;
10- ////import org.springframework.web.multipart.MultipartFile;
11- //import popcong.app.application.image.port.out.ProfileImageUploadPort;
12- ////import popcong.app.global.exception.custom.BusinessException;
13- //import popcong.app.global.exception.error.ImageS3ErrorCode;
14- //
15- //import java.io.IOException;
16- //
17- //
18- //@Slf4j
19- //@Component
20- //public class S3ProfileImageUploadAdapter implements ProfileImageUploadPort {
21- //
22- // private final AmazonS3 s3;
23- // private final String bucket = "popcongbucket";
24- // private final Regions region = Regions.US_EAST_2; //오하이오
25- //
26- // public S3ProfileImageUploadAdapter() {
27- // this.s3 = AmazonS3ClientBuilder.standard()
28- // .withRegion(region)
29- // .withCredentials(DefaultAWSCredentialsProviderChain.getInstance())
30- // .build();
31- // }
32-
33- // @Override
34- // public String uploadProfileImage(Long userId, MultipartFile file) {
35- // try {
36- // String key = buildKey(userId, file.getOriginalFilename());
37- //
38- // ObjectMetadata meta = new ObjectMetadata();
39- // meta.setContentLength(file.getSize());
40- // meta.setContentType(file.getContentType());
41- //
42- // PutObjectRequest req = new PutObjectRequest(bucket, key, file.getInputStream(), meta);
43- // s3.putObject(req);
44- //
45- // return "https://%s.s3.%s.amazonaws.com/%s"
46- // .formatted(bucket, region.getName(), key);
47- //
48- // } catch (IOException e) {
49- // log.error("S3 업로드 실패", e);
50- // throw new BusinessException(ImageS3ErrorCode.FILE_UPLOAD_ERROR);
51- // }
52- // }
53- // private String buildKey(Long userId, String original) {
54- // String safe = (original == null ? "unknown" : original).replaceAll("\\s+", "_");
55- // return "profiles/%d/%d_%s".formatted(userId, System.currentTimeMillis(), safe);
56- // }
57- //}
1+ package popcong .app .adapter .out .image ;
2+ import com .amazonaws .AmazonServiceException ;
3+ import com .amazonaws .SdkClientException ;
4+ import com .amazonaws .auth .DefaultAWSCredentialsProviderChain ;
5+ import com .amazonaws .regions .Regions ;
6+ import com .amazonaws .services .s3 .AmazonS3 ;
7+ import com .amazonaws .services .s3 .AmazonS3ClientBuilder ;
8+ import com .amazonaws .services .s3 .model .ObjectMetadata ;
9+ import com .amazonaws .services .s3 .model .PutObjectRequest ;
10+ import lombok .extern .slf4j .Slf4j ;
11+ import org .springframework .stereotype .Component ;
12+ import org .springframework .web .multipart .MultipartFile ;
13+ import popcong .app .application .image .port .out .ProfileImageUploadPort ;
14+ import popcong .app .global .exception .custom .BusinessException ;
15+ import popcong .app .global .exception .error .ImageS3ErrorCode ;
16+
17+ import java .io .IOException ;
18+
19+
20+ @ Slf4j
21+ @ Component
22+ public class S3ProfileImageUploadAdapter implements ProfileImageUploadPort {
23+
24+ private final AmazonS3 s3 ;
25+ private final String bucket = "popcongbucket" ;
26+ private final Regions region = Regions .US_EAST_2 ; //오하이오
27+
28+ public S3ProfileImageUploadAdapter () {
29+ this .s3 = AmazonS3ClientBuilder .standard ()
30+ .withRegion (region )
31+ .withCredentials (DefaultAWSCredentialsProviderChain .getInstance ())
32+ .build ();
33+ }
34+
35+ @ Override
36+ public String uploadProfileImage (Long userId , MultipartFile file ) {
37+ String key = buildKey (userId , file .getOriginalFilename ());
38+
39+ try {
40+ ObjectMetadata meta = new ObjectMetadata ();
41+ meta .setContentLength (file .getSize ());
42+ meta .setContentType (file .getContentType ());
43+
44+ PutObjectRequest req = new PutObjectRequest (bucket , key , file .getInputStream (), meta );
45+
46+ // ✅ 예외 상세 로그 추가
47+ try {
48+ s3 .putObject (req );
49+ return "https://%s.s3.%s.amazonaws.com/%s"
50+ .formatted (bucket , region .getName (), key );
51+ } catch (AmazonServiceException e ) {
52+ // S3가 에러 응답을 보낸 경우 (권한, 리전 불일치 등)
53+ log .error ("[S3 SERVICE] status={}, code={}, msg={}, bucket={}, key={}" ,
54+ e .getStatusCode (), e .getErrorCode (), e .getErrorMessage (), bucket , key , e );
55+ throw new BusinessException (ImageS3ErrorCode .FILE_UPLOAD_ERROR );
56+ } catch (SdkClientException e ) {
57+ // 네트워크, 자격증명 문제
58+ log .error ("[S3 CLIENT] msg={}, bucket={}, key={}" , e .getMessage (), bucket , key , e );
59+ throw new BusinessException (ImageS3ErrorCode .FILE_UPLOAD_ERROR );
60+ }
61+
62+ } catch (IOException e ) {
63+ log .error ("[S3 IO] msg={}, bucket={}, key={}" , e .getMessage (), bucket , key , e );
64+ throw new BusinessException (ImageS3ErrorCode .FILE_UPLOAD_ERROR );
65+ }
66+ }
67+
68+ private String buildKey (Long userId , String original ) {
69+ String safe = (original == null ? "unknown" : original ).replaceAll ("\\ s+" , "_" );
70+ return "profiles/%d/%d_%s" .formatted (userId , System .currentTimeMillis (), safe );
71+ }
72+ }
0 commit comments