Commit d838ac07 authored by changxiangyu's avatar changxiangyu

文件类型检验AOP编写

parent d9048818
package com.yeejoin.amos.boot.biz.common.annotation;
import com.yeejoin.amos.boot.biz.common.enums.FileType;
import java.lang.annotation.*;
/**
* @author chang xiangyu
* @description 文件校验
* 使用示例 -> @FileCheck(message = "不支持的文件格式",supportedFileTypes = {FileType.XLS, FileType.XLSX})
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface FileCheck {
/**
* 校验不通过提示信息
*
* @return
*/
String message() default "不支持的文件格式";
/**
* 校验方式
*/
CheckType type() default CheckType.SUFFIX;
/**
* 支持的文件后缀
*
* @return
*/
String[] supportedSuffixes() default {};
/**
* 支持的文件类型
*
* @return
*/
FileType[] supportedFileTypes() default {};
enum CheckType {
/**
* 仅校验后缀
*/
SUFFIX,
/**
* 校验文件头(魔数)
*/
MAGIC_NUMBER,
/**
* 同时校验后缀和文件头
*/
SUFFIX_MAGIC_NUMBER
}
}
\ No newline at end of file
package com.yeejoin.amos.boot.biz.common.aop;
import com.yeejoin.amos.boot.biz.common.annotation.FileCheck;
import com.yeejoin.amos.boot.biz.common.enums.FileType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* @description 文件校验切面
*/
@Component
@Slf4j
@Aspect
@ConditionalOnProperty(prefix = "file-check", name = "enabled", havingValue = "true")
public class FileCheckAspect {
@Before(value = "@annotation(fileCheck)")
public void before(JoinPoint joinPoint, FileCheck fileCheck) {
final String[] suffixes = fileCheck.supportedSuffixes();
final FileCheck.CheckType type = fileCheck.type();
final FileType[] fileTypes = fileCheck.supportedFileTypes();
final String message = fileCheck.message();
if (ArrayUtils.isEmpty(suffixes) && ArrayUtils.isEmpty(fileTypes)) {
return;
}
Object[] args = joinPoint.getArgs();
//文件后缀转成set集合
Set<String> suffixSet = new HashSet<>(Arrays.asList(suffixes));
for (FileType fileType : fileTypes) {
suffixSet.add(fileType.getSuffix());
}
//文件类型转成set集合
Set<FileType> fileTypeSet = new HashSet<>(Arrays.asList(fileTypes));
for (String suffix : suffixes) {
fileTypeSet.add(FileType.getBySuffix(suffix));
}
//对参数是文件的进行校验
for (Object arg : args) {
if (arg instanceof MultipartFile) {
doCheck((MultipartFile) arg, type, suffixSet, fileTypeSet, message);
} else if (arg instanceof MultipartFile[]) {
for (MultipartFile file : (MultipartFile[]) arg) {
doCheck(file, type, suffixSet, fileTypeSet, message);
}
}
}
}
//根据指定的检查类型对文件进行校验
private void doCheck(MultipartFile file, FileCheck.CheckType type, Set<String> suffixSet, Set<FileType> fileTypeSet, String message) {
if (type == FileCheck.CheckType.SUFFIX) {
doCheckSuffix(file, suffixSet, message);
} else if (type == FileCheck.CheckType.MAGIC_NUMBER) {
doCheckMagicNumber(file, fileTypeSet, message);
} else {
doCheckSuffix(file, suffixSet, message);
doCheckMagicNumber(file, fileTypeSet, message);
}
}
//验证文件头信息
private void doCheckMagicNumber(MultipartFile file, Set<FileType> fileTypeSet, String message) {
String magicNumber = readMagicNumber(file);
String fileName = file.getOriginalFilename();
String fileSuffix = fileName.substring(fileName.lastIndexOf("."));;
for (FileType fileType : fileTypeSet) {
if (magicNumber.startsWith(fileType.getMagicNumber()) && fileType.getSuffix().toUpperCase().equalsIgnoreCase(fileSuffix)) {
return;
}
}
log.error("文件头格式错误:{}",magicNumber);
throw new RuntimeException( message);
}
//验证文件后缀
private void doCheckSuffix(MultipartFile file, Set<String> suffixSet, String message) {
String fileName = file.getOriginalFilename();
String fileSuffix =fileName.substring(fileName.lastIndexOf(".")+1);;
for (String suffix : suffixSet) {
if (suffix.toUpperCase().equalsIgnoreCase(fileSuffix)) {
return;
}
}
log.error("文件后缀格式错误:{}", message);
throw new RuntimeException( message);
}
//读取文件,获取文件头
private String readMagicNumber(MultipartFile file) {
try (InputStream is = file.getInputStream()) {
byte[] fileHeader = new byte[4];
is.read(fileHeader);
return byteArray2Hex(fileHeader);
} catch (IOException e) {
log.error("文件读取错误:{}", e);
throw new RuntimeException( "读取文件失败!");
}
}
private String byteArray2Hex(byte[] data) {
StringBuilder stringBuilder = new StringBuilder();
if (ArrayUtils.isEmpty(data)) {
return null;
}
for (byte datum : data) {
int v = datum & 0xFF;
String hv = Integer.toHexString(v).toUpperCase();
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
String result = stringBuilder.toString();
return result;
}
}
\ No newline at end of file
package com.yeejoin.amos.boot.biz.common.enums;
import lombok.Getter;
import org.springframework.lang.NonNull;
/**
* @author chang xiangyu
* @description 文件类型
*/
@Getter
public enum FileType {
/**
* JPEG (jpg)
*/
JPEG("JPEG", "FFD8FF"),
JPG("JPG", "FFD8FF"),
/**
* PNG
*/
PNG("PNG", "89504E47"),
/**
* GIF
*/
GIF("GIF", "47494638"),
/**
* TIFF (tif)
*/
TIFF("TIF", "49492A00"),
/**
* Windows bitmap (bmp)
*/
BMP("BMP", "424D"),
/**
* 16色位图(bmp)
*/
BMP_16("BMP", "424D228C010000000000"),
/**
* 24位位图(bmp)
*/
BMP_24("BMP", "424D8240090000000000"),
/**
* 256色位图(bmp)
*/
BMP_256("BMP", "424D8E1B030000000000"),
/**
* XML
*/
XML("XML", "3C3F786D6C"),
/**
* HTML (html)
*/
HTML("HTML", "68746D6C3E"),
/**
* Microsoft Word/Excel 注意:word 和 excel的文件头一样
*/
XLS("XLS", "D0CF11E0"),
/**
* Microsoft Word/Excel 注意:word 和 excel的文件头一样
*/
DOC("DOC", "D0CF11E0"),
/**
* Microsoft Word/Excel 2007以上版本文件 注意:word 和 excel的文件头一样
*/
DOCX("DOCX", "504B0304"),
/**
* Microsoft Word/Excel 2007以上版本文件 注意:word 和 excel的文件头一样 504B030414000600080000002100
*/
XLSX("XLSX", "504B0304"),
/**
* Adobe Acrobat (pdf) 255044462D312E
*/
PDF("PDF", "25504446");
/**
* 后缀 大写字母
*/
private final String suffix;
/**
* 魔数
*/
private final String magicNumber;
FileType(String suffix, String magicNumber) {
this.suffix = suffix;
this.magicNumber = magicNumber;
}
@NonNull
public static FileType getBySuffix(String suffix) {
for (FileType fileType : values()) {
if (fileType.getSuffix().equals(suffix.toUpperCase())) {
return fileType;
}
}
throw new IllegalArgumentException("不支持的文件后缀 : " + suffix);
}
}
\ No newline at end of file
...@@ -14,6 +14,7 @@ import org.springframework.cloud.netflix.eureka.EnableEurekaClient; ...@@ -14,6 +14,7 @@ import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.FilterType;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableAsync;
...@@ -38,6 +39,7 @@ import java.net.UnknownHostException; ...@@ -38,6 +39,7 @@ import java.net.UnknownHostException;
@EnableSwagger2WebMvc @EnableSwagger2WebMvc
@EnableEurekaClient @EnableEurekaClient
@EnableSchedulerLock(defaultLockAtMostFor = "10m") @EnableSchedulerLock(defaultLockAtMostFor = "10m")
@MapperScan({"org.typroject.tyboot.demo.face.orm.dao*", "org.typroject.tyboot.face.*.orm.dao*", @MapperScan({"org.typroject.tyboot.demo.face.orm.dao*", "org.typroject.tyboot.face.*.orm.dao*",
"org.typroject.tyboot.core.auth.face.orm.dao*", "org.typroject.tyboot.component.*.face.orm.dao*", "org.typroject.tyboot.core.auth.face.orm.dao*", "org.typroject.tyboot.component.*.face.orm.dao*",
"com.yeejoin.amos.boot.module.**.api.mapper", "com.yeejoin.amos.boot.biz.common.dao.mapper"}) "com.yeejoin.amos.boot.module.**.api.mapper", "com.yeejoin.amos.boot.biz.common.dao.mapper"})
...@@ -58,5 +60,4 @@ public class AmosYsApplication { ...@@ -58,5 +60,4 @@ public class AmosYsApplication {
+ "Application Amos-Ys is running! Access URLs:\n\t" + "Swagger文档: \thttp://" + ip + ":" + port + "Application Amos-Ys is running! Access URLs:\n\t" + "Swagger文档: \thttp://" + ip + ":" + port
+ path + "/doc.html\n" + "----------------------------------------------------------\n"); + path + "/doc.html\n" + "----------------------------------------------------------\n");
} }
} }
package com.yeejoin.amos.boot.module.ys.biz.config;
import com.yeejoin.amos.boot.biz.common.aop.FileCheckAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author chang xiangyu 2024/10/8
* }
*/
@Configuration
@ComponentScan("com.yeejoin.amos.boot.biz.common.aop")
public class AnnotationAopImportConfig {
@Bean
public FileCheckAspect createAop() {
return new FileCheckAspect();
}
}
package com.yeejoin.amos.boot.module.ys.biz.controller; package com.yeejoin.amos.boot.module.ys.biz.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yeejoin.amos.boot.biz.common.annotation.FileCheck;
import com.yeejoin.amos.boot.biz.common.controller.BaseController; import com.yeejoin.amos.boot.biz.common.controller.BaseController;
import com.yeejoin.amos.boot.biz.common.enums.FileType;
import com.yeejoin.amos.boot.module.ys.api.dto.YsEmergencyUnitDto; import com.yeejoin.amos.boot.module.ys.api.dto.YsEmergencyUnitDto;
import com.yeejoin.amos.boot.module.ys.api.entity.YsEmergencyUnit; import com.yeejoin.amos.boot.module.ys.api.entity.YsEmergencyUnit;
import com.yeejoin.amos.boot.module.ys.api.service.IYsEmergencyUnitService; import com.yeejoin.amos.boot.module.ys.api.service.IYsEmergencyUnitService;
...@@ -9,6 +11,7 @@ import com.yeejoin.amos.boot.module.ys.biz.service.impl.YsEmergencyUnitServiceIm ...@@ -9,6 +11,7 @@ import com.yeejoin.amos.boot.module.ys.biz.service.impl.YsEmergencyUnitServiceIm
import com.yeejoin.amos.feign.privilege.model.AgencyUserModel; import com.yeejoin.amos.feign.privilege.model.AgencyUserModel;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert; import org.springframework.util.Assert;
...@@ -202,4 +205,6 @@ public class YsEmergencyUnitController extends BaseController { ...@@ -202,4 +205,6 @@ public class YsEmergencyUnitController extends BaseController {
AgencyUserModel userModel = getSelectedOrgInfo().getUserModel(); AgencyUserModel userModel = getSelectedOrgInfo().getUserModel();
return iYsEmergencyUnitService.expertDataImport(file,userModel); return iYsEmergencyUnitService.expertDataImport(file,userModel);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment