對于文件上傳,相信大家都不會陌生,我們都知道,文件在上傳到服務器的過程中,都是以流的形式傳輸?shù)模诤笈_處理文件上傳的代碼中,獲得這個流,然后讀取數(shù)據(jù)流將之保存到上傳文件的臨時目錄中,如果有使用到MongoDB,再將這個文件存儲到文件系統(tǒng)中。
大部分的文件上傳都是通過HTML的上傳組件完成的,而業(yè)務需求往往是有類型要求的,比如只允許上傳jpg、gif、png類型的圖片,或者是只允許上傳Office文檔等等,雖然可以用JavaScript對上傳文件做一些類型驗證之類的控制,但還是不能完全做到過慮。這時,就需要在后臺,用代碼來進一步完成這個驗證工作。
到底通過什么方式可以做到正確驗證呢?答案就是通過文件的頭部信息,通過大量測試,大象發(fā)現(xiàn)每種類型的文件,他們最開始的一段信息都是一樣的,比如Office97~03,它的頭四位16進制信息就是d0 cf 11 e0,而Office2007則是50 4b 03 04,PDF為25 50 44 46,大家可以多用這樣的文件分別測試一下,看看前四位16進制信息是不是都是一樣的。當然這其中也有個別情況,比如jpg類型的圖片,它的前四位16進制信息就有兩種一個是ff d8 ff e0,另一個是ff d8 ff e1,區(qū)別是最后一位。知道了這些,我們就有一個方向了。
可能有同學有疑問了,為什么只取前四位,不是六位或八位呢?這是因為,大象根據(jù)反復測試發(fā)現(xiàn),從第五位開始到第八位,同一種類型的文件,在這幾位里面很有一些存在區(qū)別,像圖片以及pdf,這種現(xiàn)象很多,為了避免同一類型的文件,因為這一些小的不同,要定義N多檢測頭信息,這樣做似乎沒有必要,因此大象才建議取前四位作為類型檢測的依據(jù)。
不過說了這么多,還是沒講怎么做,這顯然不是大象的風格,大象一般都從實際出發(fā),用代碼來說話。
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;
}
}下面我們準備些文件來測試一下這段代碼有沒有問題。
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 {
/**
* 文件頭部信息,十六進制信息,取前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,運行測試,結果就是前四個成功,最后一個失敗,這達到了我們的預期,只允許FILE_TYPE里面定義的文件類型通過測試。大家可以自己動手試驗一下。
本文為菠蘿大象原創(chuàng),如要轉載請注明出處。http://bolo.blogjava.net/