Java 讀取圖片文件的類型(MimeType)


一、問題描述

在項目開發的時候,我們經常會遇到一類文件上傳的問題,就是獲取圖片是哪種格式。很多情況下,很多人都是用后綴名去判斷,如下所示。

if(filename.endsWith(".png") || filename.endsWith(".jpg"))
{
   //保存圖片

}else{
   throw new IOException("Error file format !");

}

但是這種方式相當不可靠,我們可以嘗試將zip文件、rmvb文件、css、js修改后綴名位jpg或者png上傳,也可以上傳到服務器,這就造成我們服務器上出現了臟數據。此外,對於有些圖片文件,修改成錯誤的擴展名,有些瀏覽器可能無法顯示出此圖片。

二、解決方案

在計算機系統中,媒體類型(MimeType)的文件都有【標識符】,zip、圖片本身屬於媒體文件,因此我們可以通過編解碼的方式判斷圖片是否合法。

1、判斷標示方法

private static boolean isBMP(byte[] buf){
        byte[] markBuf = "BM".getBytes();  //BMP圖片文件的前兩個字節
        return compare(buf, markBuf);
    }
    
    private static boolean isICON(byte[] buf) {
        byte[] markBuf = {0, 0, 1, 0, 1, 0, 32, 32};
        return compare(buf, markBuf);
    }
    private static boolean isWEBP(byte[] buf) {
        byte[] markBuf = "RIFF".getBytes(); //WebP圖片識別符
        return compare(buf, markBuf);
    }

    private static boolean isGIF(byte[] buf) {
        
        byte[] markBuf = "GIF89a".getBytes(); //GIF識別符
        if(compare(buf, markBuf))
        {
            return true;
        }
        markBuf = "GIF87a".getBytes(); //GIF識別符
        if(compare(buf, markBuf))
        {
            return true;
        }
        return false;
    }

    
    private static boolean isPNG(byte[] buf) {
        
        byte[] markBuf = {(byte) 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A}; //PNG識別符
         // new String(buf).indexOf("PNG")>0 //也可以使用這種方式
        return compare(buf, markBuf);
    }

    private static boolean isJPEGHeader(byte[] buf) {
        byte[] markBuf = {(byte) 0xff, (byte) 0xd8}; //JPEG開始符
        
        return compare(buf, markBuf);
    }
    
    private static boolean isJPEGFooter(byte[] buf)//JPEG結束符
    {
        byte[] markBuf = {(byte) 0xff, (byte) 0xd9}; 
        return compare(buf, markBuf);
    }

2、核心方法

/**
     * 獲取文件的mimeType
     * @param filename
     * @return
     */
    private static String getMimeType(String filename){
        try {
            String mimeType = readType(filename);
            return String.format("image/%s", mimeType);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 讀取文件類型
     * @param filename
     * @return
     * @throws IOException
     */
    private static String readType(String filename) throws IOException {
        
        FileInputStream fis = null;
        try {
            File f = new File(filename);
            if(!f.exists() || f.isDirectory() || f.length()<8) {
                throw new IOException("the file ["+f.getAbsolutePath()+"] is not image !");
            }
            
            fis= new FileInputStream(f);
            byte[] bufHeaders = readInputStreamAt(fis,0,8);
            if(isJPEGHeader(bufHeaders))
            {    
                long skiplength = f.length()-2-8; //第一次讀取時已經讀了8個byte,因此需要減掉
                byte[] bufFooters = readInputStreamAt(fis, skiplength, 2);
                if(isJPEGFooter(bufFooters))
                {
                    return "jpeg";
                }
            }
            if(isPNG(bufHeaders))
            {
                return "png";
            }
            if(isGIF(bufHeaders)){
                
                return "gif";
            }
            if(isWEBP(bufHeaders))
            {
                return "webp";
            }
            if(isBMP(bufHeaders))
            {
                return "bmp";
            }
            if(isICON(bufHeaders))
            {
                return "ico";
            }
            throw new IOException("the image's format is unkown!");
            
        } catch (FileNotFoundException e) {
            throw e;
        }finally{
            try {
                if(fis!=null) fis.close();
            } catch (Exception e) {
            }
        }
        
    }

    
    /**
     * 標示一致性比較
     * @param buf  待檢測標示
     * @param markBuf 標識符字節數組
     * @return 返回false標示標示不匹配
     */
    private static boolean compare(byte[] buf, byte[] markBuf) {
        for (int i = 0; i < markBuf.length; i++) {
            byte b = markBuf[i];
            byte a = buf[i];
            
            if(a!=b){
                return false;
            }
        }
        return true;
    }
    /**
     * 
     * @param fis 輸入流對象
     * @param skiplength 跳過位置長度
     * @param length 要讀取的長度
     * @return 字節數組
     * @throws IOException
     */
    private static byte[] readInputStreamAt(FileInputStream fis, long skiplength, int length) throws IOException
    {
        byte[] buf = new byte[length];
        fis.skip(skiplength);  //
        int read = fis.read(buf,0,length);
        return buf;
    }
    

3、測試代碼

正常測試

public class ImageType {

    
    public static void main(String[] args) {
        
            String filename = "oschina.jpg";
            String type = getMimeType(filename);
            System.out.println(type);
    }
}

輸出

image/jpeg

修改擴展名測試

①修改oschina.jpeg為oschina.png

②復制oschina.png刪除擴展名

public class ImageType {

    
    public static void main(String[] args) {
        
            String filename = "oschina.png";
            String type = getMimeType(filename);
            System.out.println(type);
        
            filename = "oschina";
            type = getMimeType(filename);
            System.out.println(type);
            
    }
}

輸出

image/jpeg
image/jpeg



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM