一、问题描述
在项目开发的时候,我们经常会遇到一类文件上传的问题,就是获取图片是哪种格式。很多情况下,很多人都是用后缀名去判断,如下所示。
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