Skip to content

Commit

Permalink
csv 파일 업로드 dependency 및 기능 추가 (#328)
Browse files Browse the repository at this point in the history
  • Loading branch information
juno-junho authored May 17, 2024
1 parent 6444b1f commit 8cb6e2f
Show file tree
Hide file tree
Showing 17 changed files with 127 additions and 39 deletions.
16 changes: 12 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,31 @@ dependencies {
// concurrent-trees for trie
implementation 'com.googlecode.concurrent-trees:concurrent-trees:2.6.1'

// apache commons csv for csv generating csv file
implementation 'org.apache.commons:commons-csv:1.11.0'

// springboot
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'

// mail
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

// database
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'

// test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
testImplementation 'org.springframework.security:spring-security-test'

// mail
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

// monitoring
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import java.util.List;

import static com.spaceclub.global.annotation.profanity.BadWordExceptionMessage.BAD_WORD_DETECTED;
import static com.spaceclub.global.annotation.profanity.ProfanityExceptionMessage.BAD_WORD_DETECTED;
import static com.spaceclub.global.exception.GlobalExceptionCode.INVALID_REQUEST;
import static com.spaceclub.global.exception.GlobalExceptionCode.MAX_IMAGE_SIZE_EXCEEDED;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import static com.spaceclub.global.annotation.profanity.BadWordExceptionMessage.BAD_WORD_DETECTED;
import static com.spaceclub.global.annotation.profanity.ProfanityExceptionMessage.BAD_WORD_DETECTED;

@Component
@RequiredArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.spaceclub.global.annotation.profanity;

import lombok.Getter;

@Getter
public enum ProfanityExceptionMessage {

FAIL_BAD_WORD_SETUP("비속어 목록 Trie 생성 실패"),
BAD_WORD_DETECTED("비속어가 발견 되었습니다"),
INVALID_EXTENSION("md,txt 파일만 업로드 가능합니다."),
FAILED_TO_SAVE("금칙어 저장에 실패하였습니다."),
BAD_WORD_ALREADY_EXISTS("이미 존재하는 금칙어입니다."),
FAILED_TO_CREATE_CSV("CSV 파일 생성에 실패하였습니다.")
;

private final String message;

ProfanityExceptionMessage(String message) {
this.message = message;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import java.util.Spliterator;
import java.util.stream.StreamSupport;

import static com.spaceclub.global.annotation.profanity.BadWordExceptionMessage.FAIL_BAD_WORD_SETUP;
import static com.spaceclub.global.annotation.profanity.ProfanityExceptionMessage.FAIL_BAD_WORD_SETUP;

@Slf4j
@Component
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/spaceclub/global/aws/S3Properties.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

@ConfigurationProperties(prefix = "s3")
public record S3Properties(
String bucket,
String imageBucket,
String fileBucket,
String region,
String url,
List<String> validExtensions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
public class FileNameCreator {

private static final String DOT = ".";
private static final String CSV_EXTENSION = "csv";
private static final String FILE_FORMAT = "%s/%s_%s.%s";

private final S3Properties s3Properties;

public String createFileName(S3Folder folder, String originalName, String timestamp) {
public String createImageFileKey(S3Folder folder, String originalName, String timestamp) {
int lastDot = originalName.lastIndexOf(DOT);
String fileName = originalName.substring(0, lastDot);
String fileExtension = originalName.substring(lastDot + 1);
Expand All @@ -23,6 +24,10 @@ public String createFileName(S3Folder folder, String originalName, String timest
return String.format(FILE_FORMAT, folder.getFolder(), fileName, timestamp, fileExtension);
}

public String createCSVFileKey(S3Folder folder, String fileName, String timestamp) {
return String.format(FILE_FORMAT, folder.getFolder(), fileName, timestamp, CSV_EXTENSION);
}

private void validateFileExtension(String fileExtension) {
boolean invalidExtension = s3Properties.validExtensions()
.stream()
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/com/spaceclub/global/aws/s3/S3FileUploader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.spaceclub.global.aws.s3;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.spaceclub.global.aws.S3Properties;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import static com.spaceclub.global.exception.GlobalExceptionCode.FAIL_FILE_UPLOAD;

@Component
@RequiredArgsConstructor
public class S3FileUploader {

private static final String DATE_FORMAT = "yyyyMMdd_HHmmss";
private static final String CSV_FILE_NAME = "profanity_info";

private final AmazonS3 amazonS3;
private final S3Properties s3Properties;

public String uploadProfanityInfo(String content) {
final String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern(DATE_FORMAT));
String csvFileKey = new FileNameCreator(s3Properties).createCSVFileKey(S3Folder.PROFANITY_INFO, CSV_FILE_NAME, timestamp);
ObjectMetadata objectMetaData = createMetaData(content);

try (InputStream inputStream = new ByteArrayInputStream(content.getBytes())) {
final PutObjectRequest request = new PutObjectRequest(s3Properties.fileBucket(), csvFileKey, inputStream, objectMetaData)
.withCannedAcl(CannedAccessControlList.PublicRead);
amazonS3.putObject(request);

return s3Properties.url() + csvFileKey;
} catch (IOException e) {
throw new IllegalStateException(FAIL_FILE_UPLOAD.toString());
}
}

private ObjectMetadata createMetaData(String content) {
ObjectMetadata objectMetaData = new ObjectMetadata();
objectMetaData.setContentType("text/csv");
objectMetaData.setContentLength(content.length());
return objectMetaData;
}

}
4 changes: 3 additions & 1 deletion src/main/java/com/spaceclub/global/aws/s3/S3Folder.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ public enum S3Folder {
LOGO("club-logo"),
COVER("club-cover"),
USER_PROFILE("user-profile-image"),
POST_IMAGE("post-image");
POST_IMAGE("post-image"),
PROFANITY_INFO("profanity"),
;

private final String folder;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ public String upload(MultipartFile image, S3Folder folder) {
if (originalFilename == null) throw new IllegalArgumentException(FAIL_FILE_UPLOAD.toString());

String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern(DATE_FORMAT));
String fileName = new FileNameCreator(s3Properties).createFileName(folder, originalFilename, timestamp);
String fileName = new FileNameCreator(s3Properties).createImageFileKey(folder, originalFilename, timestamp);
ObjectMetadata objectMetaData = createMetaData(image);

try (InputStream inputStream = image.getInputStream()) {
amazonS3Client.putObject(
new PutObjectRequest(s3Properties.bucket(), fileName, inputStream, objectMetaData)
new PutObjectRequest(s3Properties.imageBucket(), fileName, inputStream, objectMetaData)
.withCannedAcl(CannedAccessControlList.PublicRead));
} catch (IOException e) {
throw new IllegalStateException(FAIL_FILE_UPLOAD.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

@ConfigurationProperties(prefix = "profanity")
public record ProfanityConfig(
String filePath
String filePath,
List<String> validExtensions
) {

}
6 changes: 5 additions & 1 deletion src/main/resources/application-develop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ oauth:
admin-key-prefix: KakaoAK

s3:
bucket: ENC(JoDpjsUcxIviI7KX5vVl47Ey3PAFn6GffGgyuKeWoek=)
file-bucket: ENC(62oqQc22U4+n1L8xyyRqtb10UWqpPwChIQ7WHe6gJ3s=)
image-bucket: ENC(JoDpjsUcxIviI7KX5vVl47Ey3PAFn6GffGgyuKeWoek=)
region: ENC(906H8BMOAumiA+H0nevzxJUoUGzBG2Nh)
url: ENC(iMleKm/uvFwQ7k0EGbj94AFJkend3u7iqYYb8QxSNMPU/nCmbB1kXG/PdovxHoc41yeIHkhSwjfjGSyp5DlMmHEqib5VWr6LkptpQ9KX9Hs=)

Expand All @@ -46,3 +47,6 @@ web:

profanity:
file-path: /home/ubuntu/bad_word_list.txt
valid-extensions:
- txt
- md
6 changes: 5 additions & 1 deletion src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ oauth:
admin-key-prefix: KakaoAK

s3:
bucket: ENC(JoDpjsUcxIviI7KX5vVl47Ey3PAFn6GffGgyuKeWoek=)
image-bucket: ENC(JoDpjsUcxIviI7KX5vVl47Ey3PAFn6GffGgyuKeWoek=)
file-bucket: ENC(62oqQc22U4+n1L8xyyRqtb10UWqpPwChIQ7WHe6gJ3s=)
region: ENC(906H8BMOAumiA+H0nevzxJUoUGzBG2Nh)
url: ENC(iMleKm/uvFwQ7k0EGbj94AFJkend3u7iqYYb8QxSNMPU/nCmbB1kXG/PdovxHoc41yeIHkhSwjfjGSyp5DlMmHEqib5VWr6LkptpQ9KX9Hs=)

Expand All @@ -51,3 +52,6 @@ web:

profanity:
file-path: src/main/resources/secrets/bad_word_list.txt
valid-extensions:
- txt
- md
12 changes: 6 additions & 6 deletions src/test/java/com/spaceclub/global/s3/FileNameCreatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ class FileNameCreatorTest {
@Test
void 폴더명_파일명_타임스탬프_확장자_형식으로_파일명을_생성한다() {
// given
S3Properties s3Properties = new S3Properties("s3bucket", "s3region", "s3url", List.of("jpg", "jpeg", "png"));
S3Properties s3Properties = new S3Properties("s3ImageBucket", "s3FileBucket", "s3region", "s3url", List.of("jpg", "jpeg", "png"));
FileNameCreator fileNameCreator = new FileNameCreator(s3Properties);
S3Folder folder = S3Folder.COVER;
String folderName = folder.getFolder();
String originalFileName = "originalName.jpg";
String timestamp = "202404191111";

// when
String fileName = fileNameCreator.createFileName(folder, originalFileName, timestamp);
String fileName = fileNameCreator.createImageFileKey(folder, originalFileName, timestamp);

// then
assertThat(fileName).isEqualTo(folderName + "/originalName_202404191111.jpg");
Expand All @@ -40,14 +40,14 @@ class FileNameCreatorTest {
@Test
void 확장자가_jpg_jpeg_png_가_아니면_예외를_생성한다() {
// given
S3Properties s3Properties = new S3Properties("s3bucket", "s3region", "s3url", List.of("jpg", "jpeg", "png"));
S3Properties s3Properties = new S3Properties("s3ImageBucket", "s3FileBucket", "s3region", "s3url", List.of("jpg", "jpeg", "png"));
FileNameCreator fileNameCreator = new FileNameCreator(s3Properties);
S3Folder folder = S3Folder.COVER;
String originalFileName = "originalName.gif";
String timestamp = "202404191111";

// when, then
assertThatThrownBy(() -> fileNameCreator.createFileName(folder, originalFileName, timestamp))
assertThatThrownBy(() -> fileNameCreator.createImageFileKey(folder, originalFileName, timestamp))
.isInstanceOf(MultipartException.class)
.hasMessage(INVALID_FILE_EXTENSION.toString());
}
Expand All @@ -56,15 +56,15 @@ class FileNameCreatorTest {
@ValueSource(strings = {"jpg", "jpeg", "png"})
void 확장자가_jpg_jpeg_png이면_예외를_발생하지_않는다(String extension) {
// given
S3Properties s3Properties = new S3Properties("s3bucket", "s3region", "s3url", List.of("jpg", "jpeg", "png"));
S3Properties s3Properties = new S3Properties("s3ImageBucket", "s3FileBucket", "s3region", "s3url", List.of("jpg", "jpeg", "png"));
FileNameCreator fileNameCreator = new FileNameCreator(s3Properties);
S3Folder folder = S3Folder.COVER;
String originalFileName = "originalName." + extension;
String timestamp = "202404191111";

// when, then
assertThatNoException()
.isThrownBy(() -> fileNameCreator.createFileName(folder, originalFileName, timestamp));
.isThrownBy(() -> fileNameCreator.createImageFileKey(folder, originalFileName, timestamp));
}

}
3 changes: 2 additions & 1 deletion src/test/resources/application-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ spring:
defer-datasource-initialization: true

s3:
bucket: space-club-image-bucket
file-bucket: file-bucket-test
image-bucket: image-bucket-test
region: region
url: https://test.com/

Expand Down
3 changes: 3 additions & 0 deletions src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ web:

profanity:
file-path: src/main/resources/secrets/bad_word_list.txt
valid-extensions:
- txt
- md

invite:
link-prefix: "https://space-club.site/api/v1/clubs/invite/"

0 comments on commit 8cb6e2f

Please sign in to comment.