隨筆-67  評論-522  文章-0  trackbacks-0
              大象根據(jù)研究與實際項目經(jīng)驗,向大家介紹一個關于文件類型驗證的解決辦法。不清楚的朋友可以了解下,知道的不喜勿噴。
              對于文件上傳,相信大家都不會陌生,我們都知道,文件在上傳到服務器的過程中,都是以流的形式傳輸?shù)模诤笈_處理文件上傳的代碼中,獲得這個流,然后讀取數(shù)據(jù)流將之保存到上傳文件的臨時目錄中,如果有使用到MongoDB,再將這個文件存儲到文件系統(tǒng)中。
              大部分的文件上傳都是通過HTML的上傳組件完成的,而業(yè)務需求往往是有類型要求的,比如只允許上傳jpggifpng類型的圖片,或者是只允許上傳Office文檔等等,雖然可以用JavaScript對上傳文件做一些類型驗證之類的控制,但還是不能完全做到過慮。這時,就需要在后臺,用代碼來進一步完成這個驗證工作。
              到底通過什么方式可以做到正確驗證呢?答案就是通過文件的頭部信息,通過大量測試,大象發(fā)現(xiàn)每種類型的文件,他們最開始的一段信息都是一樣的,比如Office97~03,它的頭四位16進制信息就是d0 cf 11 e0,而Office2007則是50 4b 03 04PDF25 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;

              }

          }
              上面這段代碼就是用來對文件類型作驗證的方法,第一個參數(shù)是文件的字節(jié)數(shù)組,第二個就是定義的可通過類型。代碼很簡單,主要是注意中間的一處,將字節(jié)數(shù)組的前四位轉換成16進制字符串,并且轉換的時候,要先和0xFF做一次與運算。這是因為,整個文件流的字節(jié)數(shù)組中,有很多是負數(shù),進行了與運算后,可以將前面的符號位都去掉,這樣轉換成的16進制字符串最多保留兩位,如果是正數(shù)又小于10,那么轉換后只有一位,需要在前面補0,這樣做的目的是方便比較,取完前四位這個循環(huá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 {

              /**

               * 文件頭部信息,十六進制信息,取前4

               * 50 4b 03 04 office 2007+

               * d0 cf 11 e0 office 97~03

               * 25 50 44 46 pdf

               * ff d8 ff e0 jpg,部分pngjpg頭文件前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();

                  }

              }

          }
              從測試代碼可以看到文件類型,只有第五個不是正確文件,是我將一個exe文件通過改后綴為png。這個測試類,用到了commons-ioFileUtils,需要在pom中加入這個依賴,而ResourceUtilsorg.springframework.util包中,它屬于spring-core.jar,大象這個測試類是放在之前ssm3工程test里面,要想正常運行測試,要在test/resources目錄下建個file文件夾存放測試文件,最后編譯之后file及其測試文件都會在test-classes下面,所以文件查找是以classpath:開頭。
               OK,運行測試,結果就是前四個成功,最后一個失敗,這達到了我們的預期,只允許FILE_TYPE里面定義的文件類型通過測試。大家可以自己動手試驗一下。
              本文為菠蘿大象原創(chuàng),如要轉載請注明出處。http://bolo.blogjava.net/ 
          posted on 2012-05-01 11:37 菠蘿大象 閱讀(6349) 評論(0)  編輯  收藏 所屬分類: Java
          主站蜘蛛池模板: 吕梁市| 金塔县| 神木县| 兴安盟| 永清县| 铜川市| 湟中县| 尤溪县| 喜德县| 嘉峪关市| 阜新市| 安阳县| 贵德县| 开远市| 会东县| 新乡市| 临泉县| 五常市| 象山县| 庄河市| 白沙| 青神县| 韶山市| 邵阳市| 佳木斯市| 扎兰屯市| 绍兴市| 邻水| 甘德县| 东方市| 松原市| 曲松县| 乐东| 蚌埠市| 邓州市| 璧山县| 延庆县| 忻州市| 开鲁县| 湖北省| 华容县|