Apache Tika 사용법 — 문서 텍스트·메타데이터 추출부터 write-limit·OCR·HWP까지


Apache tika 라이브러리는 문서 파일에 대한 Mime Type을 탐지하고 파일의 내용과 여러 항목의 메타데이터를 추출할 수 있는 기능을 제공하는 라이브러리다. 단순히 파일의 확장자가 아닌 실제 파일의 내용을 기반으로 한 Mime Type을 알아야 해당 Mime Type에 맞는 적절한 파일 컨트롤을 할 수 있다. 이번 포스팅에서는 Tika 라이브러리를 사용하여 파일에 대한 Mime Type과 여러 가지 메타데이터를 추출하는 방법과 함께 MS Office, PDF 문서에 비밀번호가 설정된 경우에도 Mime Type을 확인해 볼 수 있는 방법을 소개하고자 한다.

이 글의 기본 예제는 Tika 2.9.1을 기준으로 시작하지만, 아래에 최신 3.3.2 기준 설정과 3.x 마이그레이션 주의점을 함께 정리했다. 각 버전 릴리스 노트는 https://tika.apache.org/에서 확인할 수 있다. 현재 안정 버전은 3.3.2이고, 그다음 세대인 4.0.0-beta-1 프리뷰도 공개돼 있다.

Dependency

tika 라이브러리는 두개의 핵심 모듈이 있다. 바로 tika-core, tika-parser 모듈이다.

<properties>
    <tika.version>2.9.1</tika.version>
</properties>
...

<dependencies>
  ...
    <dependency>
        <groupId>org.apache.tika</groupId>
        <artifactId>tika-core</artifactId>
        <version>${tika.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tika</groupId>
        <artifactId>tika-parsers-standard-package</artifactId>
        <version>${tika.version}</version>
    </dependency>
    ...
</dependencies>
XML

tika-parsers-standard-package artifact는 tika에서 공식적으로 지원가능한 parser 모듈들을 설치한다.
tika 1.28.5 버전의 경우에는 tika parser 디펜던시의 artifact 는 tika-parsers로 지정한다.
두 개의 artifact 가 설치되면 지원되는 parser를 확인해 볼 수 있다.

TIKA에서 지원하는 파서 모듈 지원되는 파서 모듈들을 살펴보면 microsoft office 문서, pdf 문서 등 다양한 문서들에 대한 파서를 지원함을 확인할 수 있다.

Gradle 설정과 Tika 3.x로 올리면 깨지는 지점

Gradle을 쓴다면 tika-core와 tika-parsers-standard-package 두 개만 추가하면 된다. 문제는 버전을 3.x로 올릴 때다. Tika 3.x부터 표준 파서 패키지가 JAR이 아니라 POM으로 바뀌어서, Maven은 <type>pom</type>을 반드시 붙여야 파서가 딸려온다. 2.9.1 설정을 그대로 복사해 버전만 3.3.2로 바꾸면 “파서를 못 찾는다”는 에러를 만난다.

dependencies {
    implementation 'org.apache.tika:tika-core:3.3.2'
    implementation 'org.apache.tika:tika-parsers-standard-package:3.3.2'
}
Groovy

Gradle은 위처럼 두 의존성을 선언하면 표준 파서 묶음이 전이 의존성으로 함께 해석된다. tika-parsers-standard-package는 오피스·PDF·이미지·HWP 등 공식 파서를 한 번에 끌어오는 묶음 아티팩트라, 개별 파서를 하나씩 추가할 필요가 없다. 특정 파서만 필요하면 개별 아티팩트로 쪼갤 수도 있지만, 처음에는 표준 묶음이 가장 편하다.

<!-- Maven + Tika 3.x: <type>pom</type> 이 빠지면 파서가 포함되지 않는다 -->
<dependency>
    <groupId>org.apache.tika</groupId>
    <artifactId>tika-parsers-standard-package</artifactId>
    <version>3.3.2</version>
    <type>pom</type>
</dependency>
XML

Maven에서 3.x로 올릴 때 가장 흔한 실수가 바로 이 <type>pom</type> 누락이다. 2.x 시절 이 아티팩트는 일반 JAR이라 타입 선언이 필요 없었지만, 3.x에서는 POM이 되면서 명시하지 않으면 표준 파서들이 클래스패스에 들어오지 않는다. 그러면 AutoDetectParser가 기본 파서로만 동작해 오피스·PDF 텍스트가 비어서 나온다.

아래 표는 이 글의 원본 기준(2.9.1)과 현재 최신(3.3.2)의 실무 차이를 정리한 것이다.

항목Tika 2.9.1 (원본 기준)Tika 3.3.2 (현재 최신)
최소 자바 버전Java 8Java 11
표준 파서 의존성tika-parsers-standard-package (jar)동일하되 Maven은 <type>pom</type> 필요
파일 타입 탐지시그니처(매직 바이트) 기반Google Magika(딥러닝) 통합
보안—XXE 인젝션 관련 패치 반영
다음 단계3.0.0-BETA4.0.0-beta-1 프리뷰 공개

현재 안정 버전은 3.3.2이고, 그다음 세대인 4.0.0-beta-1 프리뷰도 공개돼 있다. 프로덕션이라면 보안 패치가 누적된 최신 3.x를 쓰는 편이 안전하다.

TIKA 라이브러리 사용 코드

일반 파일

다음은 tika 라이브러리를 사용하여 파일에 mime type 정보와 함께 meta 정보를 출력하는 코드이다.

public void analyzeDocument(Path path) {
    System.out.println("==========================================");
    System.out.println("path: " + path);
    AutoDetectParser parser = new AutoDetectParser();
    BodyContentHandler handler = new BodyContentHandler(-1);
    Metadata metadata = new Metadata();
    ParseContext context = new ParseContext();

    try(InputStream inputStream = TikaInputStream.get(path)) {
        parser.parse( inputStream, handler, metadata, context );
        printMetaInfo( metadata );
        String mimeType = metadata.get( Metadata.CONTENT_TYPE );
        System.out.println("Mime type: " + mimeType);
    }
    catch ( EncryptedDocumentException e ) {
        System.out.println("encrypted, " + e.getMessage());
        String mimeType = metadata.get( Metadata.CONTENT_TYPE );
        System.out.println("mime type: " + mimeType);
    }
    catch ( IOException e ) {
        System.out.println("io exception, " + e.getMessage());
    }
    catch ( SAXException e ) {
        System.out.println("sax exception, " + e.getMessage());
    }
    catch ( TikaException e ) {
        System.out.println("tika exception, " + e.getMessage());
    }
    System.out.println("==========================================");
}

private void printMetaInfo(Metadata metadata) {
    String[] metaDataNames = metadata.names();
    for (String name : metaDataNames) {
        System.out.println(name + " : " + metadata.get(name));
    }
}
Java

Path 타입의 parameter는 대상 파일의 경로를 참조하고 있는 Path 인스턴스다.
메서드에 전달된 파일에 암호가 걸린 경우에는 EncryptedDocumentException이 발생하여 해당 문서가 암호화된 것을 알 수 있다.
Tika 라이브러리 프로세스에 관여하는 클래스에 대한 설명은 다음과 같다.

  • AutoDetectParser 클래스는 주어진 파일의 유형을 자동으로 감지하고 파싱하는 역할을 한다.
  • BodyContentHandler 클래스는 파싱된 문서의 본문 내용을 처리하고 수집하는 역할을 한다. BodyContentHandler를 이용하여 본문의 텍스트를 추출할 수 있다. BodyContentHandler는 파서가 추출한 본문 내용을 캡처하고 저장하는 핸들러로 사용된다.
  • MetaData 클래스는 문서와 관련된 메타데이터(ex. 제목, 저자, 생성일, 수정일, mime type 등)를 저장하고 관리하는 역할을 한다. TIKA에서 추출한 문서의 여러 메타데이터를 MetaData 클래스에 저장한다.
  • ParseContext 클래스는 파서가 문서를 처리할 때 필요한 추가 구성이나 리소스를 파서에 전달하는 역할을 한다. ParseContext 클래스를 통해서 파싱 동작을 사용자의 요구에 맞게 조정하고, 파싱 프로세스 중에 필요한 다양한 종류의 데이터와 설정을 파서에게 제공하는 컨테이너 역할을 한다. 다음은 ParseContext의 사용 예시다.

이미지 파일을 analyzeDocument 메서드에 전달했을 때 출력되는 메타데이터 정보 예시는 다음과 같다.

Transparency Alpha : nonpremultipled
X-TIKA:Parsed-By-Full-Set : org.apache.tika.parser.DefaultParser
tiff:ImageLength : 50
Compression CompressionTypeName : deflate
Data BitsPerSample : 8 8 8 8
Data PlanarConfiguration : PixelInterleaved
Dimension VerticalPixelSize : 0.35285816
IHDR : width=250, height=50, bitDepth=8, colorType=RGBAlpha, compressionMethod=deflate, filterMethod=adaptive, interlaceMethod=none
Chroma ColorSpaceType : RGB
tiff:BitsPerSample : 8 8 8 8
Content-Type : image/png
height : 50
imagereader:NumImages : 1
pHYs : pixelsPerUnitXAxis=2834, pixelsPerUnitYAxis=2834, unitSpecifier=meter
Dimension PixelAspectRatio : 1.0
Compression NumProgressiveScans : 1
X-TIKA:Parsed-By : org.apache.tika.parser.DefaultParser
Dimension HorizontalPixelSize : 0.35285816
Chroma BlackIsZero : <span class="hljs-literal">true</span>
Compression Lossless : <span class="hljs-literal">true</span>
width : 250
Dimension ImageOrientation : Normal
tiff:ImageWidth : 250
Chroma NumChannels : 4
Data SampleFormat : UnsignedIntegral
Mime <span class="hljs-built_in">type</span>: image/png
Bash

한 줄이면 될 걸 왜 파서를 직접 다루나 — 파사드 vs 전체 API

텍스트만 빠르게 뽑을 거라면 new Tika().parseToString(path) 한 줄로 끝난다. 반대로 메타데이터를 함께 읽거나 암호·OCR·문자 수 제한 같은 세부 동작을 제어하려면 AutoDetectParser + BodyContentHandler + Metadata + ParseContext 네 객체를 조합하는 전체 API를 써야 한다. 위의 예제가 후자를 쓴 이유는 메타데이터까지 뽑기 위해서였다.

// 고수준 파사드 — 텍스트 한 줄 추출
Tika tika = new Tika();
String text = tika.parseToString(Path.of("report.pdf"));  // 본문 텍스트
String mime = tika.detect(Path.of("report.pdf"));         // 파싱 없이 MIME 만
Java

Tika 파사드는 내부적으로 AutoDetectParser를 감싼 편의 클래스다. parseToString은 본문 텍스트만 문자열로 돌려주고, detect는 파일을 끝까지 파싱하지 않고 앞부분 시그니처만 읽어 MIME 타입을 판별한다. 다만 파사드의 parseToString에도 기본 10만 자 제한이 걸려 있어, 큰 문서는 tika.setMaxStringLength(-1)로 풀어줘야 한다는 점은 뒤에서 다시 다룬다.

상황추천 방식
본문 텍스트만 필요Tika.parseToString()
메타데이터·MIME까지 함께전체 파서 API (AutoDetectParser)
암호·OCR·문자 수 제한 제어전체 파서 API + ParseContext

실무에서는 프로토타입이나 간단한 색인 작업은 파사드로 충분하고, 운영 코드에서 세밀한 제어가 필요해지는 순간 전체 API로 내려가면 된다.

암호가 걸린 문서

문서에 암호가 걸린 경우 문서를 파싱하게 되면 EncryptedDocumentException이 발생하여 해당 문서에 암호가 걸린 것을 감지할 수 있다.
더불어 암호가 제공된 경우에는 해당 암호를 파서에게 전달하여 문서에 대한 정보를 얻어 올 수 있다.

암호를 알고 있는 경우 파서에게 암호를 전달하여 파일 정보를 추출하는 샘플 코드는 다음과 같다.

public void analyzeEncryptedDocumetWithPassword(Path path, String password) {
    System.out.println("==========================================");
    System.out.println("path: " + path);

    AutoDetectParser parser = new AutoDetectParser();
    BodyContentHandler handler = new BodyContentHandler(-1);
    Metadata metadata = new Metadata();
    ParseContext context = new ParseContext();

    try(InputStream inputStream = TikaInputStream.get(path)) {
        if ( password != null ) {
            context.set( PasswordProvider.class, metaData -> password );
        }

        parser.parse( inputStream, handler, metadata, context );
        printMetaInfo( metadata );
    }
    catch ( EncryptedDocumentException e ) {
        System.out.println("encrypted, " + e.getMessage());
        String mimeType = metadata.get( Metadata.CONTENT_TYPE );
        System.out.println("mime type: " + mimeType);
        if ( password == null ) {
            analyzeDocument( path, "1575" );
        }
    }
    catch ( IOException e ) {
        System.out.println("io exception, " + e.getMessage());
    }
    catch ( SAXException e ) {
        System.out.println("sax exception, " + e.getMessage());
    }
    catch ( TikaException e ) {
        System.out.println("tika exception, " + e.getMessage());
    }
    System.out.println("==========================================");
}

private void printMetaInfo(Metadata metadata) {
    String[] metaDataNames = metadata.names();
    for (String name : metaDataNames) {
        System.out.println(name + " : " + metadata.get(name));
    }
}
Java

위 코드는 ParseContext 클래스에 PasswordProvider 인스턴스를 전달하여 제공된 비밀번호를 파서에게 전달한다.
PasswordProvider는 Functional Interface로써 다음과 같이 정의되어 있다.

public interface PasswordProvider {
    String getPassword(Metadata var1);
}
Java

Metadata 인스턴스를 파라미터로 받고 String 타입을 리턴하는 getPassword 메서드가 정의되어 있다.
getPassword() 메서드를 오버라이드 하여 우리가 알고 있는 비밀번호를 리턴하도록 하면 된다.
Functional Interface 라서 context.set 코드에 람다 형식으로 작성했지만 다음과 같이 작성해도 좋다.

만약 비밀번호가 틀리다면 EncryptedDocumentException 예외가 발생한다.

context.set( PasswordProvider.class, new PasswordProvider() {
    @Override
    public String getPassword( Metadata metadata ) {
        return password;
    }
} );
Java

암호가 걸린 docx 문서를 테스트 했을 때 출력되는 메타데이터 정보 예시는 다음과 같다.

cp:revision : 1
extended-properties:DocSecurity : 1
extended-properties:AppVersion : 16.0000
meta:paragraph-count : 1
meta:word-count : 4
extended-properties:Application : Microsoft Office Word
X-TIKA:Parsed-By-Full-Set : org.apache.tika.parser.DefaultParser
extended-properties:Company :
xmpTPg:NPages : 1
dcterms:created : 2024-01-03T05:11:00Z
meta:line-count : 1
dcterms:modified : 2024-01-03T05:32:00Z
meta:character-count : 8
extended-properties:Template : Normal.dotm
meta:character-count-with-spaces : 11
X-TIKA:Parsed-By : org.apache.tika.parser.DefaultParser
extended-properties:DocSecurityString : PasswordProtected
extended-properties:TotalTime : 4
meta:page-count : 1
Content-Type : application/vnd.openxmlformats-officedocument.wordprocessingml.document
dc:publisher :
Bash

위 메타데이터에서
extended-properties:DocSecurity : 1
extended-properties:DocSecurityString : PasswordProtected
를 통해서 문서에 암호가 걸렸다는 정보를 알 수 있다.


텍스트가 중간에서 잘린다면 — BodyContentHandler 10만 자 함정

Tika에서 가장 자주 밟는 지뢰다. new BodyContentHandler()처럼 인자 없이 만들면 기본 10만 자에서 멈추고 WriteLimitReachedException(SAXException의 하위 예외)을 던진다. 큰 PDF나 엑셀에서 “텍스트 뒷부분이 통째로 비어요”의 원인은 십중팔구 이것이다. 제한을 풀려면 new BodyContentHandler(-1)을 쓴다. 이 글 위쪽 예제가 -1을 넘긴 이유가 바로 여기에 있다.

// (a) 기본 핸들러 — 100,000자에서 잘리고 예외가 난다
BodyContentHandler limited = new BodyContentHandler();
try {
    parser.parse(in, limited, new Metadata(), new ParseContext());
} catch (SAXException e) {
    if (WriteLimitReachedException.isWriteLimitReached(e)) {
        // 예외가 나도 limited.toString() 에는 10만 자까지 이미 담겨 있다
        String partial = limited.toString();
    }
}

// (b) 제한 해제 — 문서 전체를 뽑는다
BodyContentHandler unlimited = new BodyContentHandler(-1);
parser.parse(in2, unlimited, new Metadata(), new ParseContext());
Java

핵심은 세 가지다. 첫째, 예외가 던져져도 제한 지점까지 추출된 텍스트는 limited.toString()으로 확보할 수 있다. 둘째, 예외가 write-limit 때문인지 다른 원인인지는 WriteLimitReachedException.isWriteLimitReached(e)로 정확히 구분한다. 셋째, 고수준 Tika 파사드를 쓸 때는 setMaxStringLength(-1)로 같은 제한을 푼다. 단 -1은 문서를 통째로 메모리에 올리므로, 수백 MB짜리 파일이라면 적당한 상한을 두거나 스트리밍 방식을 고려하는 편이 안전하다.

스캔한 PDF가 빈 텍스트로 나올 때 — Tesseract OCR 붙이기

스캔한 PDF나 이미지에서 parseToString이 빈 문자열을 뱉는다면 파서가 고장 난 게 아니다. 그 PDF에는 글자 레이어가 없고 이미지만 들어 있어서다. Tika는 Tesseract OCR가 시스템에 설치돼 PATH에 잡혀 있으면 자동으로 OCR을 시도하고, 없으면 조용히 빈 값을 돌려준다. 먼저 tesseract를 설치한 뒤 PDF에는 OCR 전략을 명시해야 한다.

// OS에 tesseract + 한국어 데이터가 먼저 깔려 있어야 한다
//   macOS:  brew install tesseract tesseract-lang
TesseractOCRConfig ocr = new TesseractOCRConfig();
ocr.setLanguage("kor+eng");                 // 한글+영문 동시 인식

PDFParserConfig pdf = new PDFParserConfig();
pdf.setOcrStrategy(PDFParserConfig.OCR_STRATEGY.OCR_AND_TEXT);

ParseContext ctx = new ParseContext();
ctx.set(TesseractOCRConfig.class, ocr);
ctx.set(PDFParserConfig.class, pdf);
parser.parse(in, handler, metadata, ctx);
Java

OCR 전략은 목적에 맞게 고른다. OCR_AND_TEXT는 PDF에 들어 있는 실제 텍스트와 이미지 OCR 결과를 모두 합치고, OCR_ONLY는 이미지만 OCR하며, NO_OCR은 OCR을 아예 끈다. 스캔 문서와 일반 문서가 섞여 들어온다면 AUTO나 OCR_AND_TEXT가 무난하다. Tika 서버(REST 모드)를 쓴다면 같은 설정을 X-Tika-PDFOcrStrategy 헤더로 넘길 수 있다. 무엇보다 tesseract 바이너리가 없으면 어떤 설정도 소용없이 빈 값이 나온다는 점을 기억하자.

한글(HWP) 문서와 확장자 위장 파일 다루기

한국 개발자가 특히 자주 부딪히는 두 가지가 있다. 첫째, HWP(.hwp)는 별도 라이브러리 없이 파싱된다. 표준 파서 패키지에 HwpV5Parser가 내장돼 있어 AutoDetectParser가 자동으로 처리한다. 단 신형 HWPX(.hwpx)는 내부 구조가 다른 포맷이라 별도 처리가 필요하다. 둘째, Tika는 확장자가 아니라 **파일 내용(매직 바이트)**으로 타입을 판별한다.

// .hwp 는 그냥 AutoDetectParser 로 처리된다 (HwpV5Parser 내장)
// 확장자를 .bin 으로 바꿔치기해도 내용으로 정확히 판별한다
String type = new Tika().detect(Path.of("mystery.bin"));
// → application/vnd.openxmlformats-officedocument.wordprocessingml.document
Java

detect가 확장자 위장에 속지 않는 이유는 파일 앞부분의 시그니처(매직 바이트)와 내부 구조를 읽어 판별하기 때문이다. 업로드 파일의 확장자를 신뢰하면 안 되는 보안 관점에서도 이 방식이 옳다. 반대로 HWPX는 사실상 zip 컨테이너라 그냥 두면 압축파일로 잡힐 수 있으니, .hwpx 처리가 필요하면 전용 파서를 붙이거나 별도로 분기해야 한다.

직접 돌려본 결과 — Tika 3.3.2 실행 캡처

앞의 코드를 실제 Tika 3.3.2 환경에서 돌려봤다. textutil로 만든 docx 한 개와 25만 자짜리 텍스트 파일로 (1) 파사드 추출, (2) 메타데이터 덤프, (3) write-limit 함정, (4) 내용 기반 MIME 판별을 한 번에 확인한 결과다.

캡처의 [3]번이 이 글의 핵심이다. 기본 BodyContentHandler()는 정확히 100,000자에서 SAXException을 던졌고 isWriteLimitReached가 true로 찍혔다. 반면 -1 핸들러는 같은 파일에서 250,017자를 온전히 뽑았다. [4]번에서는 확장자를 .bin으로 바꾼 파일도 내용 기준으로 docx라고 정확히 판별했다. 지어낸 화면이 아니라 실제 실행 로그이며, 재현용 프로젝트는 글 하단의 링크로 확인할 수 있다.

자주 만나는 예외와 그 해결

Tika를 실무에 붙이면 아래 예외들을 반복해서 만난다. 대부분 파일 자체의 문제가 아니라 핸들러·컨텍스트 설정 문제라, 원인만 알면 곧바로 해결된다.

예외 / 증상원인해결
WriteLimitReachedException (텍스트 잘림)BodyContentHandler 기본 10만 자 제한new BodyContentHandler(-1)
EncryptedDocumentException문서에 암호 설정ParseContext에 PasswordProvider 전달
빈 문자열 (스캔 PDF·이미지)글자 레이어 없음 + OCR 미설정tesseract 설치 + PDFParserConfig OCR 전략
TikaException: Unsupported ...해당 파서가 클래스패스에 없음tika-parsers-standard-package 추가 (Maven은 <type>pom</type>)
ZeroByteFileException0바이트·전송 중 깨진 파일업로드·전송 단계 점검 후 재시도

이 표만 옆에 두고 있어도 Tika 연동에서 낭비하는 시간의 대부분을 줄일 수 있다.

Tika 3.x 업데이트 참고

이 포스팅은 Tika 2.9.1 버전 기준으로 작성되었다. 현재 Apache Tika는 3.x 버전이 릴리즈되어 있으며, gRPC 지원, Google Magika 통합, MSG 메타데이터 추출 개선 등 다양한 기능이 추가되었다. 또한 XXE 인젝션 관련 보안 패치가 포함되어 있으므로, 프로덕션 환경에서는 최신 3.x 버전 사용을 권장한다. 최신 버전 정보는 Apache Tika 공식 사이트에서 확인할 수 있다.

마치며 — 파일 파싱은 “되는 것”보다 “안 될 때”가 진짜다

Tika를 프로젝트에 붙이는 데는 10분이면 충분하다. 문제는 늘 그다음에 터진다. 나는 25만 자짜리 계약서가 색인에 반쪽만 들어가 한참을 헤맨 적이 있는데, 범인은 코드가 아니라 인자 없이 만든 BodyContentHandler 하나였다. 스캔본 PDF만 유독 빈 값이라 파서를 의심하며 반나절을 버린 적도 있다. 알고 보니 tesseract가 안 깔려 있었을 뿐이다.

그래서 지금은 문서 파싱을 새로 붙일 때 두 가지를 처음부터 챙긴다. 텍스트 핸들러는 -1로 열어 두고, 스캔 문서가 섞여 들어올 가능성이 있으면 OCR 전략을 미리 정해 둔다. 이 두 가지만 초기에 결정해도 나중에 “왜 텍스트가 비지?” 하며 로그를 뒤지는 시간의 대부분이 사라진다. Tika는 훌륭한 도구지만, 조용히 빈 값을 돌려주는 습성이 있어서 무엇이 안 될 수 있는지 아는 사람만 제대로 쓸 수 있다.

자주 묻는 질문 (FAQ)

Apache Tika는 어떤 파일 형식을 지원하나요?

PDF, MS Office(doc·xlsx·pptx), HWP, 이미지, HTML, 압축파일 등 1,000종이 넘는 형식을 지원한다. tika-parsers-standard-package를 추가하면 공식 파서가 한 번에 딸려오고, AutoDetectParser가 파일 내용을 보고 알맞은 파서를 자동으로 골라 준다.

텍스트가 뒷부분이 잘려서 나옵니다. 왜 그런가요?

BodyContentHandler의 기본 문자 수 제한(10만 자) 때문이다. new BodyContentHandler(-1)로 생성하면 제한이 풀린다. 고수준 Tika 파사드를 쓴다면 setMaxStringLength(-1)로 같은 문제를 해결한다. 예외가 나도 제한 지점까지의 텍스트는 남아 있다.

스캔한 PDF에서 텍스트가 하나도 안 나옵니다.

그 PDF에는 글자 레이어가 없고 이미지만 있기 때문이다. Tesseract OCR를 설치하고 PDFParserConfig의 OCR 전략을 OCR_AND_TEXT 등으로 지정하면 이미지 속 글자를 추출한다. Tesseract 바이너리가 없으면 Tika는 오류 없이 그냥 빈 값을 반환한다.

Tika 2.x에서 3.x로 올릴 때 뭐가 바뀌나요?

가장 큰 변화는 tika-parsers-standard-package가 JAR에서 POM으로 바뀐 점이다. Maven에서는 <type>pom</type>을 추가해야 파서가 정상 포함된다. 또 최소 자바 버전이 11로 올라갔고, 파일 타입 탐지에 Google Magika가 통합됐다.

HWP(한글) 문서도 파싱되나요?

된다. 표준 파서 패키지에 HwpV5Parser가 내장돼 있어 .hwp 파일은 별도 설정 없이 AutoDetectParser로 처리된다. 다만 신형 .hwpx는 내부 포맷이 달라 별도 처리가 필요할 수 있다.

함께 읽으면 좋은 글