확장자에 유연하게 대처하는 법

김형준·2025년 1월 21일

문제 설명

Postman으로 이미지 파일 다운로드 기능을 테스트해보려 했지만 아래와 같이 이미지가 깨져서 출력되기만 했다.

원인을 찾기 위해 헤더의 Content-Type을 확인해보니 Image/jpeg가 아닌 다른 값이었다.

따라서 아래와 같이 이를 간단하게 수정하니 정상적으로 출력되었다.

    @GetMapping("/download/{filename}")
    public ResponseEntity<InputStreamResource> downloadMultipartFile(@PathVariable String filename){
        return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
            .contentType(MediaType.IMAGE_JPEG)
            .body(service.downloadImage(filename));
    }

하지만 모든 이미지가 JPEG 형식으로만 전송될 것은 아니었기에, 이에 대한 대비가 필요했다.

해결 과정

우선 클라이언트는 다운로드 요청을 위해 URI 상의 Path Variable에 확장자를 포함한 이미지 이름을 서버로 전달하는 식이었다.

그렇기에 확장자 문자열 값은 문자열을 나눠 금방 구할 수 있었다.

public class FileUtil {
    public static String getRandomFileName(MultipartFile file) {
        return UUID.randomUUID() + getFileExtension(file.getOriginalFilename());
    }

    public static String getFileExtension(String fileName) {
        if (fileName != null) {
            return fileName.substring(fileName.lastIndexOf(".") + 1);
        }
        return "";
    }
}

또한 MediaType은 valueOf(String value)로 알맞은 MediaType을 반환하였기에 이를 그대로 사용하면 되었다.

하지만 사용할 수 있는 확장자를 제한해두고 싶었기에, 아래와 같이 확장자에 대한 검사 및 기본 MediaType을 지정하는 유틸 클래스를 작성하였다.

public class ExtensionUtil {
    private static final Map<String, String> EXTENSION_TO_MIME_TYPE_MAP = new HashMap<>();

    static {
        EXTENSION_TO_MIME_TYPE_MAP.put("jpg", "image/jpeg");
        EXTENSION_TO_MIME_TYPE_MAP.put("jpeg", "image/jpeg");
        EXTENSION_TO_MIME_TYPE_MAP.put("png", "image/png");
        // 필요한 확장자를 추가
    }

    public static String getMimeTypeForExtension(String extension) {
        return EXTENSION_TO_MIME_TYPE_MAP.getOrDefault(extension.toLowerCase(), "application/octet-stream");
    }
}

최종적으로는 아래와 같이 여러 확장자명을 대비할 수 있도록 컨트롤러 코드를 수정하였다.

@GetMapping("/download/{filename}")
    public ResponseEntity<InputStreamResource> downloadMultipartFile(@PathVariable String filename){
        return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
            .contentType(MediaType.valueOf(ExtensionUtil.getMimeTypeForExtension(FileUtil.getFileExtension(filename))))
            .body(service.downloadImage(filename));
    }

하지만 FileUtil 부분에서 한 가지 실수가 있었는데, getRandomFileName()로 업로드한 이미지 이름을 저장 시 확장자 앞의 .이 없어지므로 이를 이용해 다운로드 요청을 수행하면 정상적으로 수행되지 않는 것이었다.
따라서 아래와 같이 수정하기는 했지만, getFileExtension()이 .을 포함한 확장자명을 반환하도록 처리하는 것이 좋을지에 대해서는 의문이 남는다.

public class FileUtil {
    public static String getRandomFileName(MultipartFile file) {
        return UUID.randomUUID() + "." + getFileExtension(file.getOriginalFilename());
    }

    public static String getFileExtension(String fileName) {
        if (fileName != null) {
            return fileName.substring(fileName.lastIndexOf(".") + 1);
        }
        return "";
    }
}

0개의 댓글