2013年8月28日 星期三

透過 Java 偵測 MIME Type

簡易答案:試試 Apache Tika 吧!
可以透過繼承 java.nio.file.spi.FileTypeDetector 的方式覆寫 detect() 的方法
例如:(範例程式碼轉錄自 [1])
import java.io.IOException;
import java.nio.file.Path;

import org.apache.tika.Tika;

/**
* Detects the mime type of files (ideally based on marker in file content)
*/
public class FileTypeDetector extends java.nio.file.spi.FileTypeDetector {

  private final Tika tika = new Tika();

  @Override
  public String probeContentType(Path path) throws IOException {
    return tika.detect(path.toFile());
  }
}

另外從上面的用法也可以看出,其實只要產生 Tika 這個類別的實體,就可以呼叫 detect() 做所謂的 magic detection 了。

不過要注意的是,根據官方網站上的描述,雖然說對一般檔案的狀況,Tika 都可以透過藏在檔案開頭的 magic bytes 真測出檔案的類型
但這並不是總是能偵測出來的~有時也會需要依靠其他的資訊來判斷
舉例來說,官方的說明中提到 Container Aware Detection 這個問題,舉了 Word、PowerPoint 和 Apple iWork 的檔案為例
Word(*.doc)和 PowerPoint(*.ppt)的檔案實際上是使用 OLE2 包裝過的檔案類型;而 Apple iWork 是透過 ZIP 包裝的檔案
因此如果單純依照 magic bytes 做判斷,就很容易會把 *.doc 跟 *.ppt 判斷成 OLE2 檔、把 Apple iWork 判斷成 ZIP 檔。
遇到這種狀況時,Tika 建議的作法是使用 ContainerAwareDetector 去掃過整個檔案,來確認檔案的實際類型。

PS. 後來發現官方的 tutorial 文件似乎沒有更新,因為 1.3 和 1.4 版看來都沒有 ContainerAwareDetector 這個類別了。

另外在使用 Tika 之前,如果是要將 Tika 佈署到已經存在的執行環境,建議稍微注意一下 Tika 內包含的其他 library 是否跟環境中的 library 衝突
舉例來說,Tika-app 的 jar 檔本身包含了 Apache commons-lang、Apache poi、Bouncy Castle、asm 等等的 library
有可能會因為環境中已經存在這個 library,導致將 Tika 佈署上去後系統會出錯(JRE 不知道該讀哪個 library)
或者像是如果環境中使用了 Jersey(使用 asm 3.1),但是佈署了 Tika v1.4(使用 asm 4.1),會導致 asm 的版本不相容而造成 RESTful 服務失去功能。

參考資料:
1、Transparently improve Java 7 mime-type recognition with Apache Tika
2、Apache Tika
3、TikaInputStream
4、Releasing TikaInputStream resources

沒有留言: