Today I Learned
(24.07.23)[15주차] MinIO 활용 정리 - 코드 분석
S3대신 개발/테스트 환경으로 MinIO를 사용하기로 결정하여 학습하고 검색한 대로 패턴을 보면서
비지니스 로직을 Service 단에 구성을 했다.
하지만, JSON에서 한 필드의 값으로 받아온 String 타입의 텍스트를 txt 파일로 바꾼뒤에, MinIO 서버의 버킷에 저장하고 다운로드 하는 코드의 구조를 한번 더 점검하면서,
추후 필요할 경우에 능동적으로 사용할 수 있도록 분석한 부분을 정리
사전 세팅
- code 는 RequestDTO의 코드 리뷰의 코드 부분을 Spring 으로 String 타입으로 가져온 텍스트
- codReviewId는 code 부분을 제이한 코드 리뷰의 제목, 내용을 담은 객체로 이미 DB에 save에 되에서 PK인 해당 객체의 ID
- minioClient 는 이미 주입받은 형태
업로드
String filename = "codereviews-code/code-" + codeReviewId + ".txt";
if (code != null && !code.isEmpty()) {
try {
ByteArrayInputStream inputStream = new ByteArrayInputStream(code.getBytes(
StandardCharsets.UTF_8));
minioClient.putObject(
PutObjectArgs.builder()
.bucket("project-dev-bucket")
.object(filename)
.stream(inputStream, inputStream.available(), -1)
.contentType("text/plain")
.build()
);
} catch (Exception e) {
throw new CustomException(ErrorCode.FILE_UPLOAD_FAILED);
}
}
ByteArrayInputStream inputStream = new ByteArrayInputStream(code.getBytes(
StandardCharsets.UTF_8));
- code 는 문자열 String이기때문에 인코딩 타입을 지정해서 텍스트 파일로 만들 수 있도록 인코딩을 진행
- 위해서는 UTF_8 타입으로 정의하여 다양한 문자 기호를 지원하고 저장이 가능
- getBytes() String 메서드로 String을 바이트 배열=일반 문자배열 로 변환하는 메서드
- ()안에 StandardCharsets 문자 인코딩을 지정해서 바꿀 수 있음
- “UTF_8” 처럼 문자열 통째로 리터럴로 사용할 수 있지만, 명확하게 지정해서 수정이나, 코드의 흐름을 빠르게 파악하기 위해서 정확하게 지정하는게 보편적인 사용방법
- ByteArrayInputStream 바이트 배령의 스트림화를 시켜서 메모리에 있는 바이트 배열을 직접 읽고 쓸수 잇도록 바꾸는 것
- InputStream 으로 바꿔서 데이터를 읽을 수 있도록 하는 것, 여기에서는 code 의 바이트=문자배열 데이터를 읽기 위해 세팅을 하는 것
minioClient.putObject(
PutObjectArgs.builder()
.bucket("project-dev-bucket")
.object(filename)
.stream(inputStream, inputStream.available(), -1)
.contentType("text/plain")
.build()
);
- minioClient를 통해 minio 버킷으로 업로드 하는 단계
- putObject() 메서드는 ()안의 파일을 업로드시킬 수 있는 기능을 수행하는 메서드
- PutObjectArgs.builder() MinIO의 파일을 업로드할 때, 여러 정보들을 인수들로 설정하는데 사용할 수 있는 빌더 클래스의 일종
- 따라서 builder()로 생성해줘야함
- bucket 저장할 MinIO버킷 이름을 지정
- object 업로드할 파일 객체의 이름, S3와 똑같이 경로까지 포함된 동적인 이름을 가지고 있음
- 위에서는 code-객체ID.txt 파일이 버킷안에 codereviews-code 경로 안에 만들어지게 됨
- stream(inputStream, inputStream.available(), -1)
- inputStream.available() 스트림에서 읽을 수 있는 바이트 수를 환산 = 업로드 데이터 길이
- -1 업로드할 데이터의 크기를 지정하는 부분인데, -1로 지정을 하면 전체를 업로드 한다는 의미
- contentType 파일 콘텐츠 타입으로 지정
- 위에서는 txt파일로 저장하는 것
- MIME 타입으로 자유롭게 바꿀 수 있음
- Java
- 파일 확장자: .java
- MIME 타입: text/x-java-source
- Python
- 파일 확장자: .py
- MIME 타입: text/x-python
- JavaScript
- 파일 확장자: .js
- MIME 타입: application/javascript
- C++
- 파일 확장자: .cpp (또는 .h for headers)
- MIME 타입:
- text/x-c++src (for source files)
- text/x-c++hdr (for header files)
- try-catch 문을 통한 예외처리
- 업로드는 네트워크와 I/O 에대해 오류를 처리하는 부분으로 Java Spring단에서가 아닌 네트워크 MinIO 서버, 파일 읽기쓰기 문제 등 다양하게 발생할 수 있기 때문에, 예외를 발생할경우 사용자에게 빠르게 알려서 이를 처리할 수 있도록 세팅
다운로드
String code;
try (InputStream stream = minioClient.getObject(
GetObjectArgs.builder()
.bucket("project-dev-bucket")
.object(codeReview.getCode())
.build())) {
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = stream.read(buffer)) != -1) {
result.write(buffer, 0, length);
}
code = result.toString(StandardCharsets.UTF_8.name());
} catch (Exception e) {
throw new CustomException(ErrorCode.FILE_DOWNLOAD_FAILED);
}
- ResponseDTO에 담아서 보낼 code에 다운로드 받아온 txt 파일을 String화 시켜서 JSON타입으로 응답하려는 코드
- minioClient.getObject() putObject와 반대로 ()안의 내용을 바탕으로 MinIO 버킷에서 해당 파일을 가져올 수 있음
- putObject와 똑같고, InputStream 으로 일단 가져옴
- ByteArrayInputStream 은 메모리에서 시작된 데이터를 읽기 스트림으로 가져오는 것을 목적으로 다운로드시에는 맞지 않음
- try-with-resources 문의 사용
- {} 부분에는 AutoCloseable 구현 객체만 들어올 수 있는데 InputStream은 AutoCloseable 구현이 된 객체이기때문에 I/O작업에서 Resource를 close()하는데 많이 사용하는 것
- 다운로드 중에 계속 close() 없이 계속 열려있으면 리소스 누수가 발생하면서 계속 해서 메모리에 쌓이게 될 수 있기 때문에try-with-resources 문을 사용할 수 밖에 없음
- ByteArrayOutputStream 데이터를 메모리에 저장하면서 내부에서 처리할 수 있도록 하게끔 하기 위해 생성
- byte[] buffer = new byte[1024]; 1024 바이트 크기의 어떤 바이트 배열을 생성을해서 스트림에서 읽어오는 데이터들 저장하는데에 사용
- 즉 1KB를 가지고 오기 위한 배열이 되는 것
int length;
while ((length = stream.read(buffer)) != -1) {
result.write(buffer, 0, length);
}
- 1024바이트 =1KB를 읽어온 데이터를 계속 result(ByteArrayOutputStream)에 기록을 하게 되는데, stream.read(buffer)를 통해 stream=MinIO에서 buffer크기 만큼 가져온 데이터를(0부터 읽은 크기=length)만큼 result에 넣기 시작함
- 다 읽었으면 당연히 stream 자체가 다 읽혔기 때문에 length는 자동적으로 다 읽었다는 의미로 -1 이 반환이 되기 때문에 결국 모두 다 읽어올 때까지 result에 쓴다고 보면 됨
- result.toString(StandardCharsets.UTF_8.name());
- 다시 String으로 바꾸는 데 앞서서 UTF_8형식으로 인코딩을 했기 때문에 똑같은 UTF_8로 디코딩
- toString은 디코딩 타입을 무조건 문자열로 받아야하기 때문에 “UTF_8” 로 써야하지만 업로드때와 마찬가지로 명확하게 지정하기 위해서 name() 을 사용
업로드 다운로드가 정상적으로 이뤄져있고 WebUI를 통해서도 정상적올 업로드가 된 것을 확인할 수 있었다.
좀더 코드를 분석하면서 어떻게 작동이 되고 있는지를 확인하여 다양하게 추후에 확장할 수 있도록
'Today I Learned' 카테고리의 다른 글
(24.08.02)[16주차] CD Pipeline 중 GitHubs자동화 배포에 대한 정리 (0) | 2024.08.02 |
---|---|
(24.07.24)[15주차] Sprint Data JPA Tuple 객체의 사용 (2) | 2024.07.24 |
(24.07.22)[15주차] MinIO 활용 정리 (0) | 2024.07.22 |
(24.07.15)[14주차] 프로젝트 중 동시성 제어를 위한 Redisson 활용 (1) | 2024.07.15 |
(24.07.09)[13주차] @DataJpaTest 슬라이스 테스트에서의 빈 등록 (0) | 2024.07.09 |