Blog

[Spring][iLanD] [File] TroubleShooting - 파일 인코딩해서 조회하기

Category
Author
Tags
PinOnMain
1 more property
img src에서 프로젝트 내부 경로외의 시스템 경로 파일 인지 불가능한 부분
$.ajax({ url: `/api/boards/video/${videoIdField.val()}`, success: function (video) { const fileList = video.fileList; // 파일 리스트에서 마지막 파일의 파일명 가져오기 const originFileName = fileList.length > 0 ? fileList[fileList.length - 1].fileName : ''; const lastFileName = fileList.length > 0 ? fileList[fileList.length - 1].fileName.replace(/^[^_]+_/, '') : ''; // 가져온 비디오글 정보를 화면에 표시 contentElement.html(` <div> <div style="padding: 10px">👤 작성자 : ${video.videoWriter}</div> ${fileList.length > 0 ? `<!--<div style="padding: 10px">📎 첨부파일 : ${lastFileName}</div>-->` : ''} </div> ${fileList.length > 0 ? `<div style="padding: 10px"> 미리보기: <img src="/uploads/${originFileName}" alt="첨부 이미지" style="max-width: 200px; max-height: 200px;"></div>` : ''}
Java
복사
<img src="/uploads/${originFileName}" 처럼 일반 img src로 소스를 불러오면 context 경로가 이미 localhost:8080 이 잡혀있기 때문에 시스템경로로 접근 할 수 없었다.
보안상 불가능한 방법으로 결국 서버로부터 이미지 데이터 자체를 받아와야한다. 이를 억지로 구현하기 위해서 데이터 저장 시
@Value("${file.upload-dir}") private String uploadDir; private String uploadDir2 = "/Users/inyongkim/Documents/iLanD/src/main/resources/static/uploads/"; // 파일 업로드 및 저장 로직 public String uploadFile(MultipartFile file, String uniqueFileName) throws IOException { // 파일명 보호를 위해서 UUID 포함된 파일명 사용 String fullPath = uploadDir + uniqueFileName; // 파일 저장 Files.copy(file.getInputStream(), Path.of(fullPath), StandardCopyOption.REPLACE_EXISTING); // 저장된 파일을 dir2로 이동 String newFullPath = uploadDir2 + uniqueFileName; Files.copy(Path.of(fullPath), Path.of(newFullPath), StandardCopyOption.REPLACE_EXISTING); // 저장된 파일의 경로나 파일명을 반환 return fullPath; }
Java
복사
위 코드처럼 dir2로 프로젝트 내부로 이전하는 방법을 사용해서 일부 작동되도록 구현했었지만 문제가 있었다. 이미지 로드를 위해서는 서버가 재시작되어야하고 그 전까지는 JSON 타입의 요청만 가능해져서 이미지 자체가 로드되지 않았다. 이건 사실 불필요한 데이터 중복, 복사이며 일관성을 해칠 수 있다.
해결 방안
이를 해결하기 위해서는 내가 원하는 방안을 정리해야한다.
파일은 시스템 경로에 저장되어야 한다.
서버를 통해서 파일을 불러 올 수 있어야 한다.
우선 파일은 시스템 경로에만 저장, 삭제되도록 다시 리팩토링해서 간결하게 만들었다.
@Service @RequiredArgsConstructor public class FileService { @Value("${file.upload-dir}") private String uploadDir; // 파일 업로드 및 저장 로직 public String uploadFile(MultipartFile file, String uniqueFileName) throws IOException { // 파일명 보호를 위해서 UUID 포함된 파일명 사용 String fullPath = uploadDir + uniqueFileName; // 파일 저장 Files.copy(file.getInputStream(), Path.of(fullPath), StandardCopyOption.REPLACE_EXISTING); // 저장된 파일의 경로나 파일명을 반환 return fullPath; } ... }
Java
복사
이제 파일을 불러오는 메소드를 추가해야한다.
public byte[] readFile(String fileUrl) throws IOException { return Files.readAllBytes(Path.of(fileUrl)); }
Java
복사
이제 파일을 실제로 조회하는 비지니스 로직에서 해당 메소드를 연결해준다.
@RestController @RequestMapping("/api/boards/video") @RequiredArgsConstructor public class VideoController { private final VideoService videoService; private final FileService fileService; ... // 비디오 선택 조회 @GetMapping("/{videoId}") public ResponseEntity<VideoResponseDto> getVideoById(@PathVariable Long videoId) { VideoResponseDto videoResponseDto = videoService.getVideoById(videoId); // 파일 읽어와서 추가 try { byte[] fileData = fileService.readFile(videoResponseDto.getFileUrl()); String base64Encoded = Base64.getEncoder().encodeToString(fileData); videoResponseDto.setFileContent(base64Encoded); } catch (IOException e) { // 파일 읽기 실패 e.printStackTrace(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } return ResponseEntity.ok(videoResponseDto); } ... }
Java
복사
여기서 Base64로 파일은 인코딩하게 된다. 이후 ResponseDto에 파일 자체가 인코딩된 문자열들이 응답에 포함되게 된다.
그럼 실제로 암호화된 문자열들이 파일 자체가된다.
${fileList.length > 0 ? `<div style="padding: 10px">미리보기: <img src="data:image/jpeg;base64,${video.fileContent}" alt="첨부 이미지" style="max-width: 200px; max-height: 200px;"></div>` : ''}
Java
복사
img src 부분의 코드가 변경된다. data: 로 시작하며 파일 타입과 인코딩 타입, 이후 그 문자열을 넣어주면 파일 자체로 변경해준다.
이후 프로젝트 내부 정적데이터 폴더에서 해당 이미지를 삭제했으며,
저장 경로 또한 시스템 경로로 설정했다.
# File Directory file.upload-dir=/Users/inyongkim/Documents/iLanD_files/ # File Capacity spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=10MB
Java
복사
아래처럼 이미지가 정상 작동하는 것을 확인 할 수 있었다.