-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathS3Service.java
More file actions
146 lines (125 loc) · 5.53 KB
/
S3Service.java
File metadata and controls
146 lines (125 loc) · 5.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package life.mosu.mosuserver.infra.persistence.s3;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
import java.util.UUID;
import life.mosu.mosuserver.domain.file.File;
import life.mosu.mosuserver.domain.file.Folder;
import life.mosu.mosuserver.infra.persistence.s3.property.S3Properties;
import life.mosu.mosuserver.presentation.file.dto.FileUploadResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectTaggingRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.model.Tag;
import software.amazon.awssdk.services.s3.model.Tagging;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
@Slf4j
@Service
@RequiredArgsConstructor
public class S3Service {
private static final int MAX_FILENAME_LENGTH = 150;
private static final int MAX_S3_KEY_LENGTH = 255;
private final S3Client s3Client;
private final S3Presigner s3Presigner;
private final S3Properties s3Properties;
public FileUploadResponse uploadFile(MultipartFile file, Folder folder) {
String sanitizedName = sanitizeFileName(file.getOriginalFilename());
String randomPrefix = UUID.randomUUID().toString();
String s3Key = folder.getPath() + "/" + randomPrefix + "_" + sanitizedName;
if (s3Key.length() > MAX_S3_KEY_LENGTH) {
int excess = s3Key.length() - MAX_S3_KEY_LENGTH;
sanitizedName = sanitizedName.substring(0, sanitizedName.length() - excess);
s3Key = folder.getPath() + "/" + randomPrefix + "_" + sanitizedName;
}
try {
s3Client.putObject(
PutObjectRequest.builder()
.bucket(s3Properties.getBucketName())
.key(s3Key)
.tagging("status=temp")
.contentType(file.getContentType())
.build(),
RequestBody.fromInputStream(file.getInputStream(), file.getSize())
);
} catch (IOException e) {
throw new RuntimeException("파일 스트림을 읽는 중 오류 발생", e);
} catch (S3Exception e) {
throw new RuntimeException("S3 업로드 실패", e);
}
return FileUploadResponse.of(file.getOriginalFilename(), s3Key);
}
public void deleteFile(File file) {
try {
s3Client.deleteObject(DeleteObjectRequest.builder()
.bucket(s3Properties.getBucketName())
.key(file.getS3Key())
.build());
} catch (S3Exception e) {
throw new RuntimeException("S3 파일 삭제 실패", e);
}
}
public void updateFileTagToActive(String key) {
PutObjectTaggingRequest tagReq = PutObjectTaggingRequest.builder()
.bucket(s3Properties.getBucketName())
.key(key)
.tagging(Tagging.builder()
.tagSet(List.of(Tag.builder().key("status").value("active").build()))
.build())
.build();
s3Client.putObjectTagging(tagReq);
}
public String getUrl(File file) {
return file.isPublic()
? getPublicUrl(file.getS3Key())
: getPreSignedUrl(file.getS3Key());
}
public String getPublicUrl(String s3Key) {
return String.format("https://%s.s3.amazonaws.com/%s", s3Properties.getBucketName(), s3Key);
}
public String getPreSignedUrl(String s3Key) {
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
.bucket(s3Properties.getBucketName())
.key(s3Key)
.build();
Duration expirationDuration = Duration.ofMinutes(
s3Properties.getPreSignedUrlExpirationMinutes()
);
GetObjectPresignRequest preSignRequest = GetObjectPresignRequest.builder()
.getObjectRequest(getObjectRequest)
.signatureDuration(expirationDuration)
.build();
return s3Presigner.presignGetObject(preSignRequest).url().toString();
}
private String sanitizeFileName(String originalFilename) {
String encoded = URLEncoder.encode(originalFilename, StandardCharsets.UTF_8)
.replaceAll("\\+", "%20");
// 파일명만 잘라내기 (확장자 유지)
String extension = "";
int dotIndex = encoded.lastIndexOf('.');
if (dotIndex != -1) {
extension = encoded.substring(dotIndex);
encoded = encoded.substring(0, dotIndex);
}
if (encoded.length() > MAX_FILENAME_LENGTH) {
encoded = encoded.substring(0, MAX_FILENAME_LENGTH);
}
return encoded;
}
private String shortenKey(String key) {
if (key.length() <= 40) {
return key;
}
return key.substring(0, 10) + "..." + key.substring(key.length() - 20);
}
}