💋 문제상황
private String saveFile(final MultipartFile file) {
String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
final Path path = Paths.get("src", "main", "pdf", fileName);
try {
Files.copy(file.getInputStream(), path);
final Resource resource = resourceLoader.getResource("file:" + path); // TODO: なんで「ファイル」だって
return resource.getURI().toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
사용자로부터 받은 pdf 파일을 내 서버의 특정 경로인 path
에 복사하고, 그 뒤에 path의 uri를 반환하는 메서드다. form으로 받은 파일을 저장하기 위해서 사용하고 있다.
사실 저건 gpt한테 물어봐서 짰던 코드인데, 문제가 발생함..
이후에 저기로부터 반환받은 값을 그대로 저장해 버린다.
@Transactional
public Long register(final MultipartFile file) {
final String pdfURI = saveFile(file);
log.info("Saved pdf File in Server. File URI: {}", pdfURI);
// 'file:'이 붙은 코드 그대로 저장해버림!!!
final Translations translations = translationsRepository.save(Translations.of(pdfURI, file.getOriginalFilename()));
// 이제부터 알아서 할일 함...
eventPublisher.publishEvent(new OcrRegisterEvent(this, translations));
return translations.getId();
}
이후에 데이터베이스에서 경로를 데려왔을 때, 내가 원하는 모양이 아니다,,,! 관련 이슈
저 못생긴 file:
이게 대체 왜 붙는 것인지, 모르겠어서 그냥 없애봤다.
final Resource resource = resourceLoader.getResource(String.valueOf(path));
근데 에러가 난다.
java.lang.RuntimeException: java.io.FileNotFoundException: ServletContext resource [/src/main/pdf/baa5bc59-c095-4bf7-a724-92c922d34b14_28번.pdf] cannot be resolved to URL because it does not exist
💋 문제원인
대체 이 둘의 차이가 뭐지?
final Resource resource = resourceLoader.getResource("file:" + path);
final Resource resource = resourceLoader.getResource(String.valueOf(path));
첫 번째 코드에서는 "file:" 프로토콜을 명시적으로 써줘서 경로(path
)가 파일 시스템 경로일 것으로 가정하고, resourceLoader
는 파일 시스템에서 저 경로로 가서 리소스를 반환한다.
그러나 두 번째 코드에서는 "file:" 프로토콜을 사용하지 않고 path
만을 사용하여 리소스를 로드하기 때문에 path
가 파일 시스템 경로인지, 또는 다른 유형의 리소스 경로인지 확실히 알지 못한다. 만약 path
가 파일 시스템 경로가 아니라면 이렇게 작성된 코드는 오류를 발생시킬 수 있다고는 하는데,,,
사실 저 파일이 제대로 저장은 되어 있거덩? 근데 명시적으로 어떤 유형의 리소스인지를 알려주는게 더 좋을 것 같다.
💋 해결방법
Resource
라는 인터페이스가 너무 여러 리소스에 대해 범용적이어서 발생하는 문제다.
Spring Framework의 Resource
인터페이스는 다양한 유형의 리소스를 추상화하기 위해 설계된거니깐, 난 굳이 파일을 쓰는게 명백한데 여기서 저 젤 꼭대기 인터페이스를 쓸 필요는 없을 것 같다.
실제로도 리소스 종류는 저래 다양하니 아마도 내가 썼던 두번째 코드는 1, 2 중에 구분을 못하는 것 같다.
나는 그래서 FileSystemResource
를 사용하도록 코드를 수정했다.
private String saveFile(final MultipartFile file) {
String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
final Path path = Paths.get("src", "main", "pdf", fileName);
try {
Files.copy(file.getInputStream(), path);
Resource resource = new FileSystemResource(path.toFile());
return resource.getFile().getPath();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
"file:" 프로토콜을 명시할 필요도 없어져서 코드가 더 명확하고 간결해졌다. 파일 시스템에서 리소스를 로드하는 데 특화된 클래스를 사용하는 것이 더 바람직하다!
도움이 되었다면, 공감/댓글을 달아주면 깃짱에게 큰 힘이 됩니다!🌟
비밀댓글과 메일을 통해 오는 개인적인 질문은 받지 않고 있습니다. 꼭 공개댓글로 남겨주세요!
'Spring' 카테고리의 다른 글
[Spring] 스프링 설정파일에서 @Value로 API KEY 등 변수 가져오기 (0) | 2024.02.08 |
---|---|
[Spring/DB] 예외 발생 시 트랜잭션은 commit될까, rollback될까? (0) | 2023.11.11 |
[Spring/AOP] 프록시 객체가 아닌 내부 호출 시 트랜잭션이 적용되지 않는 이슈 (0) | 2023.11.06 |
[Spring/DB] JDBC, SQL Mapper(JdbcTemplate, MyBatis), ORM(JPA - Hybernate): 차이점과 등장 배경, 장단점에 대하여 (2) | 2023.06.16 |
[Spring] 빈 스코프(Scope): 싱글톤, 프로토타입, 웹 관련 스코프 (0) | 2023.06.15 |