判斷用戶上傳文件的合法性僅僅通過后綴名是完全不夠的,誰也不知道后綴名是否被更改,服務器保存一個不知道真實類型的文件有極大的風險。
因此需要后台進行進一步的文件類型校驗,這里有兩種情況:
1)一般的文件類型例如:jpg、png、xlsx等等是有固定文件頭的,提取出用戶上傳文件的文件頭與固定文件頭進行對比,就可以得到文件的准確類型。
2)文本文件:txt、csv等等。文本文件具有特殊性,文本頭無明顯標志。但是文本文件百分百具有業務特殊性。可以在對文本進行格式驗證時,判斷是否為目標類型;或者通過編碼校驗,看文本是否正確編碼,是否有亂碼存在(正確編輯的文件,不可能存在亂碼),有第三方插件cpdetector可以檢測當前文件的編碼;最后還可以通過提供文件模板,固定文本文件的頭尾等方式來進行校驗。
這里主要說明第一種情況,第二種文本情況根據業務具體環境不同,有不同的處理方式。
文件類型枚舉👇
package com.uti.utilEnum; public enum FileTypeEnum { JPG("ffd8ffe000104a464946"), PNG("89504e470d0a1a0a0000"), GIF("47494638396126026f01"), TIF("49492a00227105008037"), BMP_1("424d228c010000000000"),//16色位圖(bmp) BMP_2("424d8240090000000000"),//24位位圖(bmp) BMP_3("424d8e1b030000000000"),//256色位圖(bmp) DWG("41433130313500000000"), HTML("3c21444f435459504520"), HTM("3c21646f637479706520"), CSS("48544d4c207b0d0a0942"), JS("696b2e71623d696b2e71"), RTF("7b5c727466315c616e73"), PSD("38425053000100000000"), EML("46726f6d3a203d3f6762"), DOC("d0cf11e0a1b11ae10000"), VSD("d0cf11e0a1b11ae10000"), MDB("5374616E64617264204A"), PS("252150532D41646F6265"), PDF("255044462d312e350d0a"), RMVB("2e524d46000000120001"), RM("2e524d46000000120001"), FLV("464c5601050000000900"), F4V("464c5601050000000900"), MP4("00000020667479706d70"), MP3("49443303000000002176"), MPG("000001ba210001000180"), WMV("3026b2758e66cf11a6d9"), ASF("3026b2758e66cf11a6d9"), WAV("52494646e27807005741"), AVI("52494646d07d60074156"), MID("4d546864000000060001"), ZIP("504b0304140000000800"), RAR("526172211a0700cf9073"), INI("235468697320636f6e66"), JAR("504b03040a0000000000"), EXE("4d5a9000030000000400"), JSP("3c25402070616765206c"), MF("4d616e69666573742d56"), XML("3c3f786d6c2076657273"), SQL("494e5345525420494e54"), JAVA("7061636b616765207765"), BAT("406563686f206f66660d"), GZ("1f8b0800000000000000"), PROPERTIES("6c6f67346a2e726f6f74"), CLASS("cafebabe0000002e0041"), CHM("49545346030000006000"), MXP("04000000010000001300"), DOCX("504b0304140006000800"), WPS("d0cf11e0a1b11ae10000"), TORRENT("6431303a637265617465"), MOV("6D6F6F76"), WPD("FF575043"), DBX("CFAD12FEC5FD746F"), PST("2142444E"), QDF("AC9EBD8F"), PWL("E3828596"), RAM("2E7261FD"); private String value = ""; private FileTypeEnum(String value) { this.value = value; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } }
具體代碼👇
private boolean notTextFileTypeCheck(InputStream inputStream, String specifiedType) throws IOException { boolean fileTypeIsVaild = false; byte[] buffer = new byte[10]; inputStream.read(buffer); //獲取當前文件的真實類型 String curfileType = getTrueFileType(bytesToHexFileTypeString(buffer)); //指定文件類型中是否匹配當前文件類型 if(specifiedType.toUpperCase().equals(curfileType)){ fileTypeIsVaild = true; } return fileTypeIsVaild; } private String getTrueFileType(String s) { for (FileTypeEnum fileTypeEnum : FileTypeEnum.values()) { if (s.startsWith(fileTypeEnum.getValue())) { return fileTypeEnum.toString(); } } return null; } private String bytesToHexFileTypeString(byte[] buffer) { StringBuilder hexFileTypeStr = new StringBuilder(); for (byte b : buffer) { String hexString = Integer.toHexString(b & 0xFF); if (hexString.length() < 2) { hexFileTypeStr.append("0"); } hexFileTypeStr.append(hexString); } return hexFileTypeStr.toString(); }
關於為什么要&0xFF,推薦一篇文章https://www.cnblogs.com/think-in-java/p/5527389.html,加上評論更好理解,還能回顧下“原碼反碼補碼”的知識