Java 에서 다운로드받은 이미지의 타입 유추하기
개요
팀에서 Feign을 통해 이미지 다운로더를 구현해 사용하고 있다.
그런데 해당 클래스에 대해, 다운받은 이미지의 형식을 검증해달라는 요구사항이 추가되었다.
팀에서 요구한 사항은 png, jpg, bmp 에 대해 다운로드를 허용하고 이외엔 차단하는 것인데,
이를 구현하면서 겪은 문제를 간단히 공유해본다.
요구사항
- png(png, apng), jpeg(jpg, jpeg), bmp 를 제외한 다른 파일 다운로드 금지
- 기존 DTO에 fileType이라는 필드를 추가로 전달
- 해당 클라이언트를 사용하는 서비스에 validation 로직 추가
이제 위의 요구사항 충족을 위해, 이미지 파일의 타입을 알아내고자 한다.
이미지 파일 타입 유추
파일의 타입을 유추하는 방법은 크게 다음과 같다.
header 정보 참조
이미지 파일은 응답 헤더에 Content-Type 이라는 값을 전달한다.
현재 요구사항에 맞는 파일은 아래의 형식을 가진 파일이다.
- image/png
- image/jpeg
- image/bmp
문제사항
다만 이 방식에는 한 가지 문제가 있다.
응답을 주는 서버에서, MINE Type 값을 적절히 설정하지 않고 보내는 경우가 존재한다.
예를 들어, 어떤 파일은 바로 다운로드가 가능하도록 application/octet-stream 값을 전달하고 있어 이미지 타입을 유추할 수 없다.
이러한 이유로, 단순하게 헤더값을 사용하는 것은 불가하다.
파일 분석
guessContentTypeFromStream
java.base 내부 클래스 중 하나인 URLConnection의 guessContenttypeFromStream 은, 파일의 바이너리 값을 바탕으로 타입을 유추한다.
조건에 일치하는 타입이 있다면, 해당 타입의 MIME Type을 String 값으로 반환한다.
아래는 그 코드 중 일부를 발췌한 것이다.
if (c1 == 'G' && c2 == 'I' && c3 == 'F' && c4 == '8') {
return "image/gif";
}
if (c1 == '#' && c2 == 'd' && c3 == 'e' && c4 == 'f') {
return "image/x-bitmap";
}
if (c1 == '!' && c2 == ' ' && c3 == 'X' && c4 == 'P' &&
c5 == 'M' && c6 == '2') {
return "image/x-pixmap";
}
if (c1 == 137 && c2 == 80 && c3 == 78 &&
c4 == 71 && c5 == 13 && c6 == 10 &&
c7 == 26 && c8 == 10) {
return "image/png";
}
(...)
다만 해당 메소드 사용에도 문제점이 있다.
- 해당 로직에는 bmp에 대한 검증 로직이 없다.
- 요구사항에 bmp가 포함되어 있으므로, 해당 로직 사용은 불가하다.
만약 gif, png, jpg 정도의 메이저 형식에 대한 검증만 필요하다면 이 방법으로 충분하다.
Apache tika
Apache tika 는 위의 guessContentTypeFromStream과 유사하게, 파일을 분석하여 타입을 유추해주는 라이브러리이다.
해당 라이브러리 적용 결과, bmp를 비롯하여 여러 타입을 지원하는 것을 확인할 수 있었다.
결과적으로, 아래처럼 코드 작업이 수행되었다.
val file // ByteArray class
val fileType = Tika().detect(ByteArrayInputStream(file))
Apache tika
Apache Tika 소개
Apache Tika는 다양한 유형의 파일에서 메타데이터와 텍스트를 감지하고 추출하는 데 사용되는 Java 라이브러리이다.
수천 가지 파일 형식을 지원하며, 다음과 같은 기능을 제공한다.
- 파일 형식 감지: 파일의 내용을 분석하여 파일 형식을 식별
- 메타데이터 추출: 파일 형식에 따라 제목, 저자, 생성 날짜, 키워드 등의 메타데이터를 추출
- 텍스트 추출: 이미지, PDF, 문서 등 다양한 파일 형식에서 텍스트를 추출
- 언어 감지: 추출된 텍스트의 언어를 감지
- 엔티티 추출: 사람, 장소, 조직 등의 엔티티를 추출합니다.
Apache Tika Detect
Apache Tika Detect는 파일 형식 감지 기능에 특화된 Apache Tika의 하위 모듈입니다. Detect는 다음과 같은 기능을 제공합니다.
- 빠른 파일 형식 감지: 파일의 시작 부분만 분석하여 빠르게 파일 형식을 식별
- 확장 가능한 감지기: 새로운 파일 형식 감지기를 추가 가능
- 정확한 감지: 다양한 파일 형식을 정확하게 식별
예시 코드
import org.apache.tika.Tika;
import org.apache.tika.metadata.Metadata;
public class TikaExample {
public static void main(String[] args) throws Exception {
// 파일 경로
String filePath = "/path/to/file.txt";
Tika tika = new Tika();
Metadata metadata = tika.parse(new File(filePath));
// 파일 형식
String fileType = tika.detect(new File(filePath));
System.out.println("File type: " + fileType);
// 메타데이터
String title = metadata.get("title");
String author = metadata.get("author");
System.out.println("Title: " + title);
System.out.println("Author: " + author);
}
}
마치며
이미지 업로드에 대한 파일 검증은 간단한 데에 비해, 다운로드한 이미지 파일에 대한 검증은 레퍼런스도 적고 답답한 부분이 많았다.
다행히 Apache tika 를 통해 해결했지만, 해당 라이브러리를 찾기 전까지는 불필요하게 시간이 많이 들어갔다.
그래서 이 글을 보고, 다른 분들이 도움을 얻기를 바란다.