對(duì)于文件上傳,相信大家都不會(huì)陌生,我們都知道,文件在上傳到服務(wù)器的過(guò)程中,都是以流的形式傳輸?shù)模诤笈_(tái)處理文件上傳的代碼中,獲得這個(gè)流,然后讀取數(shù)據(jù)流將之保存到上傳文件的臨時(shí)目錄中,如果有使用到MongoDB,再將這個(gè)文件存儲(chǔ)到文件系統(tǒng)中。
大部分的文件上傳都是通過(guò)HTML的上傳組件完成的,而業(yè)務(wù)需求往往是有類型要求的,比如只允許上傳jpg、gif、png類型的圖片,或者是只允許上傳Office文檔等等,雖然可以用JavaScript對(duì)上傳文件做一些類型驗(yàn)證之類的控制,但還是不能完全做到過(guò)慮。這時(shí),就需要在后臺(tái),用代碼來(lái)進(jìn)一步完成這個(gè)驗(yàn)證工作。
到底通過(guò)什么方式可以做到正確驗(yàn)證呢?答案就是通過(guò)文件的頭部信息,通過(guò)大量測(cè)試,大象發(fā)現(xiàn)每種類型的文件,他們最開(kāi)始的一段信息都是一樣的,比如Office97~03,它的頭四位16進(jìn)制信息就是d0 cf 11 e0,而Office2007則是50 4b 03 04,PDF為25 50 44 46,大家可以多用這樣的文件分別測(cè)試一下,看看前四位16進(jìn)制信息是不是都是一樣的。當(dāng)然這其中也有個(gè)別情況,比如jpg類型的圖片,它的前四位16進(jìn)制信息就有兩種一個(gè)是ff d8 ff e0,另一個(gè)是ff d8 ff e1,區(qū)別是最后一位。知道了這些,我們就有一個(gè)方向了。
可能有同學(xué)有疑問(wèn)了,為什么只取前四位,不是六位或八位呢?這是因?yàn)椋笙蟾鶕?jù)反復(fù)測(cè)試發(fā)現(xiàn),從第五位開(kāi)始到第八位,同一種類型的文件,在這幾位里面很有一些存在區(qū)別,像圖片以及pdf,這種現(xiàn)象很多,為了避免同一類型的文件,因?yàn)檫@一些小的不同,要定義N多檢測(cè)頭信息,這樣做似乎沒(méi)有必要,因此大象才建議取前四位作為類型檢測(cè)的依據(jù)。
不過(guò)說(shuō)了這么多,還是沒(méi)講怎么做,這顯然不是大象的風(fēng)格,大象一般都從實(shí)際出發(fā),用代碼來(lái)說(shuō)話。
package com.bolo.util;
public class FileValidateUtil {
public static boolean validateType(byte[] b, String customTypes) {
if (b != null) {
int size = b.length;
String hex = null;
StringBuilder contentType = new StringBuilder();
for (int i = 0; i < size; i++) {
hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = "0" + hex;
}
contentType.append(hex);
if (i > 2)
break;
}
if (customTypes.indexOf(contentType.toString()) > -1) {
return Boolean.TRUE;
}
}
return Boolean.FALSE;
}
}下面我們準(zhǔn)備些文件來(lái)測(cè)試一下這段代碼有沒(méi)有問(wèn)題。
package com.bolo.util;
import java.io.IOException;
import junit.framework.Assert;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import org.springframework.util.ResourceUtils;
import com.bolo.util.FileValidateUtil;
public class FileValidateUtilTest {
/**
* 文件頭部信息,十六進(jìn)制信息,取前4位
* 50 4b 03 04 office 2007+
* d0 cf 11 e0 office 97~03
* 25 50 44 46 pdf
* ff d8 ff e0 jpg,部分png與jpg頭文件前4位一樣
* ff d8 ff e1 jpg,一種不同的jpg頭文件
* 89 50 4e 47 png
*/
private static final String FILE_TYPE = "504b0304 d0cf11e0 25504446 ffd8ffe0 ffd8ffe1 89504e47";
@Test
public void jpgTest(){
validateType("file/1.jpg");
}
@Test
public void docTest(){
validateType("file/2.doc");
}
@Test
public void docxTest(){
validateType("file/3.docx");
}
@Test
public void pdfTest(){
validateType("file/4.pdf");
}
@Test
public void exeTest(){
validateType("file/5.png");
}
private void validateType(String path){
try {
Assert.assertTrue(FileValidateUtil.validateType(FileUtils
.readFileToByteArray(ResourceUtils.getFile("classpath:" + path)), FILE_TYPE));
} catch (IOException e) {
e.printStackTrace();
}
}
}OK,運(yùn)行測(cè)試,結(jié)果就是前四個(gè)成功,最后一個(gè)失敗,這達(dá)到了我們的預(yù)期,只允許FILE_TYPE里面定義的文件類型通過(guò)測(cè)試。大家可以自己動(dòng)手試驗(yàn)一下。
本文為菠蘿大象原創(chuàng),如要轉(zhuǎn)載請(qǐng)注明出處。http://bolo.blogjava.net/