[Spring Boot] File Upload & Download (2) - ORACLE DB 연동
++) 해당 포스팅은 이전 게시글의 코드를 수정하여 진행하므로 먼저 보고 오는 것을 권장합니다.
https://born2bedeveloper.tistory.com/65
이번에는 파일 업로드, 다운로드 로직에 Oracle DB를 연동하여 진행해볼 것이다.
JPA 방식을 사용하여 간단히 구현해보자.
Oracle & JPA dependency 및 환경 구성
build.gradle에 두 라이브러리를 추가한다.
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation group: 'com.oracle.database.jdbc', name: 'ojdbc8', version: '21.6.0.0.1'
환경설정에 DB 커넥션 설정 및 JPA 설정을 추가하자.
application.yml
server:
port: 8888
spring:
servlet:
multipart:
enabled: true
location: C:/Temp
file-size-threshold: 2KB
max-file-size: 200MB
max-request-size: 215MB
datasource:
driver-class-name: oracle.jdbc.OracleDriver
url: jdbc:oracle:thin:@localhost:1521:ORCL
username: 유저 이름
password: 비밀번호
jpa:
hibernate:
ddl-auto: update
show-sql: true
database: oracle
file:
uploadDir: ${user.home}/jimkwon
ddl-auto 설정을 통해 자동으로 테이블을 생성하게 설정해두자!
Entity 구현
테이블과 연동할 Entity를 구현하자.
domain/BaseEntity.java
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AccessLevel;
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@SuperBuilder
public abstract class BaseEntity {
@CreatedDate
@Column(updatable = false)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yy-MM-dd", timezone = "Asia/Seoul")
private LocalDateTime createdDate;
@LastModifiedDate
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yy-MM-dd", timezone = "Asia/Seoul")
private LocalDateTime modifiedDate;
}
업로드한 파일의 생성 시간, 수정 시간은 BaseEntity를 따로 만들어 상속받는 구조로 만들었다.
수정, 생성시간을 갱신하기 위해 @EntityListeners를 넣어두고, FileApplication에 다음과 같이 JpaAuditing 어노테이션을 추가해준다.
domain/File.java
package com.example.file.domain;
import javax.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder
@Entity
@Table(name="file_db")
public class File extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name="FILE_NM")
private String realName;
@Column(name="FILE_TEMP_NM")
private String uuidName;
@Column(name="FILE_EXT")
private String extension;
@Column(name="FILE_PATH")
private String filePath;
@Column(name="FILE_SIZE")
private long fileSize;
}
@Id 어노테이션을 사용할 때, 패키지 명을 주의하자.
org.springframework.data.annotation.Id 패키지는 no-sql일 경우이기 때문에
javax.persistence.Id 패키지를 import해야함
Repository 구현
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.domain.File;
public interface FileRepository extends JpaRepository<File, String>{
File findByUuidName(String uuid);
}
필자의 경우, 파일 이름을 uuid로 변환하여 넣는 컬럼이 있기 떄문에 uuidName을 이용해 해당 파일을 불러오는 메소드를 하나 추가해두었다.
uuid : 어떤 개체(데이터)를 고유하게 식별하는 데 사용되는 16바이트(128비트) 길이의 숫자
사용하는 이유? 파일의 경우 이름이 중복될 경우가 있기 때문에 구분할 필요가 있다.
Service FileUpload 코드 수정
이제 기존의 코드에서 테이블에 파일의 메타데이터를 넣는 부분을 추가해보자.
먼저 repository를 주입한 후
FileService.java
@Service
@Slf4j
public class FileService {
private final Environment env;
private final Path fileLocation;
private final FileRepository fileRepository;
@Autowired
public FileService(Environment env, FileRepository fileRepository) {
this.env = env;
this.fileLocation = Paths.get(env.getProperty("file.uploadDir")).toAbsolutePath().normalize();
this.fileRepository = fileRepository;
try {
Files.createDirectories(this.fileLocation);
} catch (Exception ex) {
throw new FileStorageException("Could not create the directory where the uploaded files will be stored.", ex);
}
}
기존에 파일을 폴더에 업로드하는 메소드에 코드를 추가한다.
public String storeFile(MultipartFile file) {
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
try {
if(fileName.contains("..")) {
throw new FileStorageException("Sorry! Filename contains invalid path sequence " + fileName);
}
Path targetLocation = this.fileLocation.resolve(fileName);
Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
File fileEntity = File.builder()
.uuidName(UUID.randomUUID().toString())
.fileSize(file.getSize())
.filePath(targetLocation.toString())
.realName(FilenameUtils.removeExtension(fileName))
.extension(FilenameUtils.getExtension(fileName))
.build();
fileRepository.save(fileEntity);
return fileName;
} catch (IOException ex) {
throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
}
}
이제 DB연동 준비가 끝났다!
실행 결과
이제 변경된 코드를 실행하면 auto-ddl로 테이블이 자동 생성될것이다. 확인해보자.
터미널에 해당 문구가 뜨고, Oracle DB를 확인해보면
테이블이 자동 생성되었다! 이제 파일 업로드를 통해 데이터를 넣어보자.
그런데 띠용? 해당 오류가 뜬다.
그 이유는 우리가 Entity에 지정해둔 기본키인 id에 대한 시퀀스가 존재하지 않는다는것..
기존에 늘 mysql로 Autoincrement로 자동증가를 해왔던 터라 Oracle의 시퀀스 문법에 익숙하지 않았다.
먼저, Oracle에서 시퀀스를 생성한다.
CREATE SEQUENCE JM_SEQ;
그 후 Entity를 수정해보자!
그 후 다시 어플리케이션을 기동하면?
야호! 이제 DB에 값이 들어갔나 확인해보자.
무사히 데이터가 들어간 것을 확인할 수 있다.
다양한 DB와 연동할 수 있으니 각자 환경에 맞춰서 설정할 수 있을 것이고, 업로드 이외에 넘어오는 인자값을 설정하여 DB에서 다양한 메타데이터를 꺼내올 수 있는 메소드를 만들 수 있을 것이다.
이후 응용은 필요한 상황에 따라 구현하면 되겠다.
파일 업로드, 다운로드 정복!
[참고 링크]
https://okky.kr/article/1211050