判断用户上传文件的合法性仅仅通过后缀名是完全不够的,谁也不知道后缀名是否被更改,服务器保存一个不知道真实类型的文件有极大的风险。
因此需要后台进行进一步的文件类型校验,这里有两种情况:
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,加上评论更好理解,还能回顾下“原码反码补码”的知识