web-dev-qa-db-ja.com

アップロードされているファイルのInputStreamのMIMEタイプを取得するにはどうすればよいですか?

簡単な質問:ユーザーがサーブレットにアップロードしているファイルについて、ファイルを保存せずにInputStreamのMIMEタイプ(またはコンテンツタイプ)を取得するにはどうすればよいですか?

25
Trick

入力ストリームをどこから取得するかによって異なります。サーブレットから取得する場合は、doPostの引数であるHttpServerRequestオブジェクトを介してアクセスできます。 Jerseyのようなある種のREST APIを使用している場合は、@ Contextを使用してリクエストを挿入できます。ソケットを介してファイルをアップロードする場合、httpヘッダーを継承しないため、プロトコルの一部としてMIMEタイプを指定する必要があります。

6
LINEMAN78

上記のライブラリが適切ではなかった、またはそれらにアクセスできなかったため、byte []に​​対して独自のコンテンツタイプ検出器を記述しました。うまくいけば、これは誰かを助けます。

// retrieve file as byte[]
byte[] b = odHit.retrieve( "" );

// copy top 32 bytes and pass to the guessMimeType(byte[]) funciton
byte[] topOfStream = new byte[32];
System.arraycopy(b, 0, topOfStream, 0, topOfStream.length);
String mimeGuess = guessMimeType(topOfStream);

...

private static String guessMimeType(byte[] topOfStream) {

    String mimeType = null;
    Properties magicmimes = new Properties();
    FileInputStream in = null;

    // Read in the magicmimes.properties file (e.g. of file listed below)
    try {
        in = new FileInputStream( "magicmimes.properties" );
        magicmimes.load(in);
        in.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    // loop over each file signature, if a match is found, return mime type
    for ( Enumeration keys = magicmimes.keys(); keys.hasMoreElements(); ) {
        String key = (String) keys.nextElement();
        byte[] sample = new byte[key.length()];
        System.arraycopy(topOfStream, 0, sample, 0, sample.length);
        if( key.equals( new String(sample) )){
            mimeType = magicmimes.getProperty(key);
            System.out.println("Mime Found! "+ mimeType);
            break;
        } else {
            System.out.println("trying "+key+" == "+new String(sample));
        }
    }

    return mimeType;
}

magicmimes.propertiesファイルの例(これらのシグネチャが正しいかどうかはわかりませんが、私の用途ではうまくいきました)

# SignatureKey                  content/type
\u0000\u201E\u00f1\u00d9        text/plain
\u0025\u0050\u0044\u0046        application/pdf
%PDF                            application/pdf
\u0042\u004d                    image/bmp
GIF8                            image/gif
\u0047\u0049\u0046\u0038        image/gif
\u0049\u0049\u004D\u004D        image/tiff
\u0089\u0050\u004e\u0047        image/png
\u00ff\u00d8\u00ff\u00e0        image/jpg
11
Kit

Real Gagnonの優れたサイト によると、このケースのより良い解決策は Apache Tika を使用することです。

8
Riduidel

Content-Typeヘッダーフィールド 使用されている ファイル名の拡張子 を確認してください。それ以外の場合は、Tikaetcによるチェックなど、より複雑なルーチンを実行する必要があります。

1
b_erb

他の場所でslf4jロギングを使用しない限り、tika-app-1.x.jarをクラスパスに追加できます。衝突が発生するためです。 tikaを使用して入力ストリームを検出する場合は、サポートされているとマークする必要があります。それ以外の場合、tikaを呼び出すと入力ストリームが消去されます。ただし、Apache IOライブラリを使用してこれを回避し、InputStreamをメモリ内のファイルに変換するだけの場合。

import org.Apache.tika.*;

Tike tika = new Tika();
InputStream in = null;
FileOutputStream out = null;
try{
   out = new FileOutputStream(c:/tmp.tmp);
   IOUtils.copy(in, out);
   String mimeType = tika.detect(out);
}catch(Exception e){
   System.err.println(e);
} finally {
   if(null != in) 
       in.close();
   if(null != out)
       out.close();
 }
1
kslote1

私は「最初に自分でやってから、ライブラリソリューションを探す」という大きな支持者です。幸いなことに、このケースはそれだけです。

ファイルの「マジックナンバー」、つまりその署名を知っている必要があります。 InputStreamがPNGファイルを表すかどうかを検出する例を挙げましょう。

PNG署名は、HEXで以下を一緒に追加することによって構成されます。

1)エラーチェックバイト

2)ASCIIのような文字列 "PNG":

     P - 0x50
     N - 0x4E
     G - 0x47

3)CR(キャリッジリターン)-0x0D

4)LF(ラインフィード)-0xA

5)SUB(代用)-0x1A

6)LF(ラインフィード)-0xA

だから、マジックナンバーは

89   50 4E 47 0D 0A 1A 0A

137  80 78 71 13 10 26 10 (decimal)
-119 80 78 71 13 10 26 10 (in Java)

137 -> -119変換の説明

Nビット数を使用して、2^N異なる値を表すことができます。 8、または2^8=256の範囲のバイト(0..255ビット)の場合。 Java 署名されるバイトプリミティブを考慮する、つまり、範囲は-128..127です。したがって、137は歌われていると見なされ、-119 = 137 - 256

コルティンの例

private fun InputStream.isPng(): Boolean {
    val magicNumbers = intArrayOf(-119, 80, 78, 71, 13, 10, 26, 10)
    val signatureBytes = ByteArray(magicNumbers.size)
    read(signatureBytes, 0, signatureBytes.size)
    return signatureBytes.map { it.toInt() }.toIntArray().contentEquals(magicNumbers)
}

もちろん、多くのMIMEタイプをサポートするためには、このソリューションをなんらかの方法でスケーリングする必要があります。結果に満足できない場合は、ライブラリを検討してください。

0

JAX-RS RESTサービスを使用している場合は、MultipartBodyから取得できます。

@POST
@Path( "/<service_path>" )
@Consumes( "multipart/form-data" )
public Response importShapeFile( final MultipartBody body ) {
    String filename = null;
    String InputStream stream = null;
    for ( Attachment attachment : body.getAllAttachments() )
    {
        ContentDisposition disposition = attachment.getContentDisposition();
        if ( disposition != null && PARAM_NAME.equals( disposition.getParameter( "name" ) ) )
        {
            filename = disposition.getParameter( "filename" );
            stream = attachment.getDataHandler().getInputStream();
            break;
        }
    }

    // Read extension from filename to get the file's type and
    // read the stream accordingly.
}

PARAM_NAMEは、ファイルストリームを保持するパラメータの名前を表す文字列です。

0
crowmagnumb