簡単な質問:ユーザーがサーブレットにアップロードしているファイルについて、ファイルを保存せずにInputStream
のMIMEタイプ(またはコンテンツタイプ)を取得するにはどうすればよいですか?
入力ストリームをどこから取得するかによって異なります。サーブレットから取得する場合は、doPostの引数であるHttpServerRequestオブジェクトを介してアクセスできます。 Jerseyのようなある種のREST APIを使用している場合は、@ Contextを使用してリクエストを挿入できます。ソケットを介してファイルをアップロードする場合、httpヘッダーを継承しないため、プロトコルの一部としてMIMEタイプを指定する必要があります。
上記のライブラリが適切ではなかった、またはそれらにアクセスできなかったため、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
Real Gagnonの優れたサイト によると、このケースのより良い解決策は Apache Tika を使用することです。
他の場所で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();
}
私は「最初に自分でやってから、ライブラリソリューションを探す」という大きな支持者です。幸いなことに、このケースはそれだけです。
ファイルの「マジックナンバー」、つまりその署名を知っている必要があります。 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タイプをサポートするためには、このソリューションをなんらかの方法でスケーリングする必要があります。結果に満足できない場合は、ライブラリを検討してください。
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は、ファイルストリームを保持するパラメータの名前を表す文字列です。