webflux 支持formdata获取参数(包括文件),以及formdata参数封装
在项目微服务的升级过程中,我们通常会设置一个网关,作为一个洪流的出入口,在Spring Cloud 中提供了对应的功能,也就是Spring Cloud Gateway。对于旧的项目springMVC,实际也就是将spring-webmvc升级为spring-webflux,但你会发现fromdata 形式的数据,在webmvc可以被封装成参数,而在webflux中却不能,是不支持吗?在spring
在项目微服务的升级过程中,我们通常会设置一个网关,作为一个洪流的出入口,在Spring Cloud 中提供了对应的功能,也就是Spring Cloud Gateway。对于旧的项目springMVC,实际也就是将spring-webmvc升级为spring-webflux,但你会发现fromdata 形式的数据,在webmvc可以被封装成参数,而在webflux中却不能,是不支持吗?
在spring官方文档中,有提及对fromdata表单数据的获取,如下图
fromdata表单数据需要通过ServerWebExchange 的getFormData()获取【也可以通过getMultipartData()方法获取数据】
主要对数据处理的代码如下:
//exchange 为 ServerWebExchange
exchange.getMultipartData().map(data ->{
Map<String, Part> stringPartMap = data.toSingleValueMap();
//表单参数名
Part name= stringPartMap.get("name");
//将name进行处理转换
处理转换代码
//返回处理后的值
return name;
});
具体的例子:比如需要从表单中取出file(MultipartFile)
@PostMapping("/api/test2")
public Object test2(ServerWebExchange exchange){
return exchange.getMultipartData().map(data ->{
Map<String, Part> stringPartMap = data.toSingleValueMap();
Part file = stringPartMap.get("file");
if(file instanceof FilePart){
File file1 = null;
try {
FilePart filePart = (FilePart)file;
file1 = new File(filePart.filename());
filePart.transferTo(file1);
//获取到MultipartFile
MultipartFile multipartFile = FileUtil.fileToMultipartFile(file1);
//业务代码块
} catch (Exception e) {
throw new RuntimeException("参数分析错误!");
}finally {
if(file1.exists()){
file1.delete();
}
}
}
return "success";
});
}
文件处理工具类
public class FileUtil {
public static MultipartFile fileToMultipartFile(File file) {
FileItem fileItem = createFileItem(file);
return new CommonsMultipartFile(fileItem);
}
public static FileInputStream fileToFileInputStream(File file) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return fileInputStream;
}
public static File filePartToFile(FilePart filePart,File file) {
file = new File(filePart.filename());
filePart.transferTo(file);
return file;
}
private static FileItem createFileItem(File file) {
FileItemFactory factory = new DiskFileItemFactory(16, null);
FileItem item = factory.createItem("textField", "text/plain", true, file.getName());
int bytesRead;
byte[] buffer = new byte[8192];
try {
FileInputStream fis = new FileInputStream(file);
OutputStream os = item.getOutputStream();
while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
return item;
}
}
直此,我们就能够获取到fromdata的数据
但这样子对于想获取fromdata数据,每次都需要引用ServerWebExchange ,然后通过getMultipartData()方法获取数据,比较臃肿,能否对数据重新进行封装呢,答案是可以的,下面我通过注解和配置封装参数解析类 进行fromdata数据的二次封装,代码案例如下:
controller层代码:
@PostMapping("/api/test2")
public String test2(@ParameterConversion(name = "file",type = MultipartFile.class) MultipartFile multipartFile,
@ParameterConversion(name = "name",type = String.class) String name,
@ParameterConversion(name = "id",type = Integer.class) Integer id){
//业务代码块
return "success";
}
注解类
@Inherited
@Documented
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface ParameterConversion {
//解析的名称
String name();
//转化的类型
Class<?> type();
}
WebConfigurer 配置类
@Configuration
public class WebConfigurer implements WebFluxConfigurer {
/**
* 加入自定义方法参数解析器
* @param configurer
*/
@Override
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
List<HttpMessageReader<?>> readers=new ArrayList<>();
//添加Http消息编解码器
readers.add(new DecoderHttpMessageReader<>(new Jackson2JsonDecoder()));
//消息编解码器与Resolver绑定
configurer.addCustomResolver(new ParamsResolver(readers));
}
}
请求参数二次解析封装
public class ParamsResolver extends AbstractMessageReaderArgumentResolver {
public ParamsResolver(List<HttpMessageReader<?>> readers) {
super(readers);
}
protected ParamsResolver(List<HttpMessageReader<?>> messageReaders, ReactiveAdapterRegistry adapterRegistry) {
super(messageReaders, adapterRegistry);
}
/**
* 判断是否需要解析参数
* @param methodParameter
* @return
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
System.out.println("into supportsParameter");
if (!methodParameter.hasParameterAnnotation(ParameterConversion.class)) {
return false;
} else {
ParameterConversion parameterAnnotation= methodParameter.getParameterAnnotation(ParameterConversion.class);
return parameterAnnotation != null && methodParameter.getParameterType().isAssignableFrom(parameterAnnotation.type())
&& Objects.nonNull(parameterAnnotation.name());
}
}
@Override
public Mono<Object> resolveArgument(MethodParameter methodParameter, BindingContext bindingContext, ServerWebExchange serverWebExchange) {
System.out.println("into resolveArgument");
ParameterConversion parameterAnnotation= methodParameter.getParameterAnnotation(ParameterConversion.class);
String name = parameterAnnotation.name();
Class<?> type = parameterAnnotation.type();
return serverWebExchange.getMultipartData().map(data -> {
Part part = data.toSingleValueMap().get(name);
if (part == null)
throw new RuntimeException("ParameterConversion fail");
Object partObj = partToClass(part, type);
Assert.notNull(partObj,"ParameterConversion fail, 不支持该类型转换");
return partObj;
});
}
private Object partToClass(Part part,Class<?> type){
if (type == MultipartFile.class && part instanceof FilePart) {
File file = null;
try {
FilePart filePart = (FilePart) part;
file = new File(filePart.filename());
filePart.transferTo(file);
return FileUtil.fileToMultipartFile(file);
} finally {
if (file != null && file.exists()) {
file.delete();
}
}
} else if (type == FilePart.class) {
return part;
}else if (type == String.class){
return bufferToStr(part.content());
} else if (type == Integer.class) {
return Integer.parseInt(bufferToStr(part.content()));
} else if (type == BigDecimal.class) {
return new BigDecimal(bufferToStr(part.content()));
} else {
return null;
}
}
private String bufferToStr(Flux<DataBuffer> content){
AtomicReference<String> res = new AtomicReference<>();
content.subscribe(buffer -> {
byte[] bytes = new byte[buffer.readableByteCount()];
buffer.read(bytes);
DataBufferUtils.release(buffer);
res.set(new String(bytes, StandardCharsets.UTF_8));
});
return res.get();
}
}
以上代码,为作者个人观点,如有不足处,请指教;
更多推荐
所有评论(0)