package com.zxtech.utils;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
public class FileFormatVerifyUtil {
private static Map<String,String> FileTypes = new HashMap<>();
static {
FileTypes.put("696b2e71623d696b2e71",".js"); // js
FileTypes.put("252150532D41646F6265",".ps");
FileTypes.put("1f8b0800000000000000",".gz");// gz文件
FileTypes.put("4d616e69666573742d56",".mf");// MF文件
FileTypes.put("ffd8ffe000104a464946",".jpg"); // JPEG (jpg)
FileTypes.put("ffd8ffe000104a4649460",".jpeg"); // JPEG (jpg)
FileTypes.put("89504e470d0a1a0a0000",".png"); // PNG (png)
FileTypes.put("D0CF11E0A1B11AE100000",".ppt"); // PPT (ppt)
FileTypes.put("504B03041400060008000",".pptx"); // PPTX (pptx)
FileTypes.put("47494638",".gif"); // GIF (gif)47494638396126026f01
FileTypes.put("49492a00227105008037",".tif"); // TIFF (tif)
FileTypes.put("424d",".bmp"); //图(bmp)
FileTypes.put("41433130313500000000",".dwg"); // CAD (dwg)
FileTypes.put("3c21646f637479706520",".htm"); // HTM (htm)
FileTypes.put("48544d4c207b0d0a0942",".css"); // css
FileTypes.put("7b5c727466315c616e73",".rtf"); // Rich Text Format (rtf)
FileTypes.put("38425053000100000000",".psd"); // Photoshop (psd)
FileTypes.put("46726f6d3a203d3f6762",".eml"); // Email [Outlook Express 6] (eml)
FileTypes.put("d0cf11e0a1b11ae1000000",".doc"); // MS Excel 注意:word、msi 和 excel的文件头一样
FileTypes.put("d0cf11e0a1b11ae10000000",".xls"); // MS Excel 注意:word、msi 和 excel的文件头一样
FileTypes.put("d0cf11e0a1b11ae10000",".vsd"); // Visio 绘图
FileTypes.put("5374616E64617264204A",".mdb"); // MS Access (mdb)
FileTypes.put("255044462d312e",".pdf");
FileTypes.put("464c5601050000000900",".flv"); // flv与f4v相同
FileTypes.put("00000020667479706d70",".mp4");
FileTypes.put("49443303000000002176",".mp3");
FileTypes.put("000001ba210001000180",".mpg"); //
FileTypes.put("3026b2758e66cf11a6d9",".wmv"); // wmv与asf相同
FileTypes.put("52494646e27807005741",".wav"); // Wave (wav)
FileTypes.put("52494646d07d60074156",".avi");
FileTypes.put("4d546864000000060001",".mid"); // MIDI (mid)
FileTypes.put("504b0304140000000800",".zip");
FileTypes.put("526172211a0700cf9073",".rar");
FileTypes.put("235468697320636f6e66",".ini");
FileTypes.put("504b03040a0000000000",".jar");
FileTypes.put("4d5a9000030000000400",".exe");// 可执行文件
FileTypes.put("3c25402070616765206c",".jsp");// jsp文件
FileTypes.put("3c3f786d6c2076657273",".xml");// xml文件
FileTypes.put("494e5345525420494e54",".sql");// xml文件
FileTypes.put("406563686f206f66660d",".bat");// bat文件
FileTypes.put("49545346030000006000",".chm");// bat文件
FileTypes.put("04000000010000001300",".mxp");// bat文件
FileTypes.put("d0cf11e0a1b11ae100000000",".wps");// WPS文字wps、表格et、演示dps都是一样的
FileTypes.put("3c21444f435459504520",".html"); // HTML (html)
FileTypes.put("2e524d46000000120001",".rmvb"); // rmvb/rm相同
FileTypes.put("7061636b616765207765",".java");// java文件
FileTypes.put("504B030414000600",".docx");// docx文件
FileTypes.put("504b0304140006000800",".xlsx");// xlsx文件
FileTypes.put("cafebabe0000002e0041",".class");// bat文件
FileTypes.put("6431303a637265617465",".torrent");
FileTypes.put("6c6f67346a2e726f6f74",".properties");// bat文件
FileTypes.put("CFAD12FEC5FD746F",".dbx"); // Outlook Express (dbx)
FileTypes.put("6D6F6F76",".mov"); // Quicktime (mov)
FileTypes.put("FF575043",".wpd"); // WordPerfect (wpd)
FileTypes.put("2142444E",".pst"); // Outlook (pst)
FileTypes.put("AC9EBD8F",".qdf"); // Quicken (qdf)
FileTypes.put("E3828596",".pwl"); // Windows pd (pwl)
FileTypes.put("2E7261FD",".ram"); // Real Audio (ram)
}
/**
* @Description 根据传入的文件获得后缀,获得指定文件格式byte[]数组中的前N位字符
* 将传入文件转化为byte[]数组,取前N位.判断传入文件的前N位和我们指定好的文件byte[]的前N位是否相同,
* 如果相同则文件格式没有被篡改,反之,文件后缀格式被篡改
* @Param [file]
* @return boolean 返回true 表示文件格式验证通过, 返回false 文件格式验证失败
**/
public static boolean suffixVerify(File file,String fileType){
//根据文件的后缀获取,获取文件的byte[]的前8位
if(FileTypes.containsValue(fileType.toLowerCase()) || 1==1){
for(Map.Entry<String, String> filetype:FileTypes.entrySet()){
if(filetype.getValue().equals(fileType)){
String fileByte8 = String.valueOf(filetype.getKey());
//获取传入文件的byte[]的前N位
byte[] bytes = inputStream2ByteArray(file);
String compareByte = bytesToHexString(bytes);
compareByte = compareByte.toUpperCase();
//如果传入文件的byte[]的前N位和我们定义好的byte[]的前N位相同,验证通过.
if (compareByte.startsWith(fileByte8.toUpperCase())){
//如果格式校验成功
return true;
}
}
}
}else{
return false;
}
return false;
}
/**
* @Description 将file文件转化为byte[]
* @Param [file]
* @return byte[]
**/
public static byte[] inputStream2ByteArray(File file){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
FileInputStream fis = null;
byte[] buffer = null;
try {
fis = new FileInputStream(file);
//不用读取全部文件,只读文件前面的部分
byte[] b = new byte[1024];
fis.read(b);
bos.write(b, 0, 1024);
buffer = bos.toByteArray();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e1){
e1.printStackTrace();
}finally {
try {
if(fis !=null){
fis.close();
}
}catch (Exception e){
e.printStackTrace();
}
try {
if(bos !=null){
bos.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
return buffer;
}
/**
* @Description 取byte[]前N位的为字符串,转成16进制,防止修改字节码
* @Param [src]
* @return java.lang.String
**/
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder();
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/*public static void main(String[] args) throws IOException {
File f = new File("E:\\工作\\22\\非法文件\\4_1624356044549.jpg");
System.out.println(FileFormatVerifyUtil.suffixVerify(f,".jpg"));
}*/
}
Controller类调用 根据业务自行选择一二三步
第一步 先验证文件真实类型
第二步 验证文件后缀名
第三步 验证文件的字节码 防止漏掉手工修改文件名和文件字节码的情况
@ApiOperation("上传文件")
@PostMapping("/uploadFile")
public ResponseEntity<Object> uploadFile(@RequestParam MultipartFile file){
//配置允许的文件doc,docx,pdf,ppt,xlsx,xls,mp4,jpg,png,jpeg
Boolean fileType = false;//文件本身的类型是否正确
//Boolean suffixType = false;
//判断上传文件的真实类型
String fileContentType = file.getContentType();
//文件本身类型配置
String fileContentTypeStr = "image/jpeg," +
"image/png," +
"application/pdf," +
"video/mp4," +
"application/msword," +
"application/vnd.openxmlformats-officedocument.wordprocessingml.document," +
"application/vnd.openxmlformats-officedocument.presentationml.presentation," +
"application/vnd.ms-excel," +
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet," +
"application/vnd.ms-powerpoint";
String fileContentTypeList[] = fileContentTypeStr.split(",");
for(String fileContentTypeName:fileContentTypeList){
if(fileContentTypeName.equals(fileContentType)){
fileType = true;
}
}
//获取文件的后缀名
//String suffix = FileUtil.getExtensionName(file.getOriginalFilename());
//根据文件名获取后缀的类型
String contentName = getFileContentType(file.getOriginalFilename());
if(!fileType){
throw new BadRequestException("文件类型不对,请重新选择文件");
}else if(fileType){
if(contentName == null ){
throw new BadRequestException("文件类型不对,请重新选择文件");
}else if(contentName != null && !contentName.equals(fileContentType)){
throw new BadRequestException("文件类型不对,请重新选择文件");
}
}
String suffixName = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
//将MultipartFile转换为File
File newFile = FileUtil.toFile(file);
if(!FileFormatVerifyUtil.suffixVerify(newFile,suffixName)){//suffixVerify返回值true 表示文件格式验证通过, 返回false 文件格式验证失败
throw new BadRequestException("文件类型不对,请重新选择文件");
};
/*//手动配置允许文件后缀名集合
String fileTypeStr = "doc,docx,pdf,ppt,xlsx,xls,mp4,jpg,png,jpeg";
String fileTypeNameList[] = fileTypeStr.split(",");
for(String fileTypeName:fileTypeNameList){
if(fileTypeName.equals(suffix)){
suffixType = true;
}
}
if(!fileType || !suffixType){
throw new BadRequestException("文件类型不对,请重新选择文件");
}*/
// 判断文件是否为图片
/*String suffix = FileUtil.getExtensionName(file.getOriginalFilename());
if(!FileUtil.IMAGE.equals(FileUtil.getFileType(suffix))){
throw new BadRequestException("只能上传图片");
}*/
//将file转化为MultipartFile,直接使用上面的MultipartFile不好用,上面的MultipartFile被转为file后变为空了
MultipartFile newMultipartFile = getMultipartFile(newFile);
LocalStorage localStorage = localStorageService.create(null, newMultipartFile);
return new ResponseEntity<>(localStorage, HttpStatus.OK);
}
/**
* 文件名获取文件的真实类型(MIME)
* @param fileName
* @return
*/
private static String getFileContentType(String fileName){
String returnFileName = fileName.substring(fileName.lastIndexOf("."));
if(returnFileName != null){
if(returnFileName.equals(".jpeg") || returnFileName.equals(".jpg")){
return "image/jpeg";
}else if(returnFileName.equals(".png")){
return "image/png";
}else if(returnFileName.equals(".mp4")){
return "video/mp4";
}else if(returnFileName.equals(".ppt")){
return "application/vnd.ms-powerpoint";
}else if(returnFileName.equals(".pptx")){
return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
}else if(returnFileName.equals(".pdf")){
return "application/pdf";
}else if(returnFileName.equals(".doc")){
return "application/msword";
}else if(returnFileName.equals(".docx")){
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
}else if(returnFileName.equals(".xlsx")){
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
}else if(returnFileName.equals(".xls")){
return "application/vnd.ms-excel";
}else{
return "other";
}
}
return null;
}
/**
* file转MultipartFile MockMultipartFile需要引入依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
* @param file
* @return
*/
private MultipartFile getMultipartFile(File file){
FileInputStream fileInputStream = null;
MultipartFile multipartFile = null;
try {
fileInputStream = new FileInputStream(file);
multipartFile = new MockMultipartFile(file.getName(),file.getName(),
ContentType.APPLICATION_OCTET_STREAM.toString(),fileInputStream);
} catch (Exception e) {
e.printStackTrace();
}
return multipartFile;
}