一、問題描述
在項目開發的時候,我們經常會遇到一類文件上傳的問題,就是獲取圖片是哪種格式。很多情況下,很多人都是用后綴名去判斷,如下所示。
1
2
3
4
5
6
|
if (filename.endsWith( ".png" ) || filename.endsWith( ".jpg" )) { //保存圖片 } else { throw new IOException( "Error file format !" ); } |
但是這種方式相當不可靠,我們可以嘗試將zip文件、rmvb文件、css、js修改后綴名位jpg或者png上傳,也可以上傳到服務器,這就造成我們服務器上出現了臟數據。此外,對于有些圖片文件,修改成錯誤的擴展名,有些瀏覽器可能無法顯示出此圖片。
二、解決方案
在計算機系統中,媒體類型的文件都有【標識符】,zip、圖片本身屬于媒體文件,因此我們可以通過編解碼的方式判斷圖片是否合法。
1、判斷標示方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
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、核心方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
/** * 獲取文件的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、測試代碼
正常測試
1
2
3
4
5
6
7
|
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刪除擴展名
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://my.oschina.net/ososchina/blog/1610685