如何获取正在上载的文件的InputStream的MIME类型?

时间:2011-01-05 08:25:50

标签: java servlets inputstream mime-types

简单的问题:对于用户上传到我的servlet的文件,如何在不保存文件的情况下获取InputStream的MIME类型(或内容类型)?

8 个答案:

答案 0 :(得分:10)

我为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

答案 1 :(得分:7)

根据Real Gagnon's excellent site,针对您的案例的更好解决方案是使用Apache Tika

答案 2 :(得分:6)

这取决于您从哪里获取输入流。如果从servlet获取它,则可以通过作为doPost参数的HttpServerRequest对象访问它。如果你正在使用像Jersey这样的某种休息API,那么可以使用@Context注入请求。如果您通过套接字上传文件,则您有责任将MIME类型指定为协议的一部分,因为您不会继承http标头。

答案 3 :(得分:1)

您可以查看Content-Type header field并查看所使用的extension of the filename。对于其他一切,您必须运行更复杂的例程,例如通过Tika等进行检查。

答案 4 :(得分:1)

只要不在其他地方使用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();
 }

答案 5 :(得分:0)

如果使用JAX-RS休息服务,您可以从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是表示保存文件流的参数名称的字符串。

答案 6 :(得分:0)

我强烈支持“先自己动手,然后寻找图书馆解决方案”。幸运的是,这种情况就是这样。

您必须知道文件的“魔术号码”,即其签名。 让我举一个例子来检测InputStream是否代表PNG文件。

PNG签名是通过在十六进制中附加以下内容组成的:

1)错误检查字节

2)字符串“ PNG”,如ASCII:

     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个不同的值。   对于82^8=256范围的字节(0..255位)。   Java 考虑要签名的字节基元,因此范围为-128..127。   因此,137被认为是单字母代表-119 = 137 - 256

Koltin中的示例

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类型,您必须以某种方式扩展此解决方案,如果对结果不满意,请考虑使用一些库。

答案 7 :(得分:-4)

我认为这解决了问题:

    public String readIt(InputStream is) {
    if (is != null) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"), 8);

            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line).append("\n");
            }
            is.close();
            return sb.toString();
    }
    return "error: ";
}        

它返回什么?例如对于png:&#34;♦PNG \ n \ n♦♦♦.....&#34;,对于xml:

非常有用,你不能尝试string.contains()来检查它是什么