SpringBoot文件上传同时,接收复杂参数

news2024/11/15 18:04:17

目录

环境信息

问题描述

错误分析

解决方法

简单参数

总结


环境信息

        Spring Boot:2.0.8.RELEASE

        Spring Boot内置的tomcat:tomcat-embed-core 8.5.37

问题描述

        收到文件上传的开发工作,要求能适配各种场景,并且各场景的请求参数不一样,因此接收的参数不能是固定的几个字段,要有类似Map的字段来接收动态参数。

        拟使用MultipartFile[] files来接收文件列表,用自定义对象UploadFileDto来接收上传参数(里面包含一个Map)

        UploadFileDto.java

@ApiModel("文件上传dto")
@Data
public class UploadFileDto {
    @ApiModelProperty("处理器,在Spring容器里的名称")
    private String handlerName;

    @ApiModelProperty("交易代码")
    private String bizCode;

    @ApiModelProperty("交易ID")
    private String bizId;

    @ApiModelProperty("上传配置名称,对应配置文件里的配置")
    private String uploadConfigName;

    @ApiModelProperty("动态信息")
    private Map<String, Object> dynamicDto;

    @ApiModelProperty("上传时间")
    private String uploadDt;

    @ApiModelProperty("限定文件大小")
    private long defaultFileSize;

    @ApiModelProperty("限定文件格式,多个用逗号隔开")
    private String defaultFileSuffix;

    @ApiModelProperty("默认路径之后的文件上级目录")
    private String parentDir;

    @ApiModelProperty("备注信息")
    private String remark;

    @ApiModelProperty("上传后的文件信息")
    private List<FileInfoDto> fileInfoDtos;

}

Controller:

@RestController
@RequestMapping("/fileCommmon")
@Api(tags = "公共-文件操作")
public class FileUpDownLoadController extends BaseController {

    @Resource
    private UpDownLoadService upDownLoadService;

    @PostMapping(value = "/upload")
    @ApiOperation("文件上传")
    public ResponseDto<UploadFileDto> uploadFile(@RequestParam("files") MultipartFile[] files, @RequestPart() UploadFileDto uploadFileDto) {
        upDownLoadService.uploadFile(uploadFileDto, files);
        return getResponseDto(uploadFileDto);
    }
}

Controller方法,使用@RequestParam("files") MultipartFile[] files来接收文件列表

使用@RequestPart() UploadFileDto uploadFileDto来接收复杂参数。

UpDownLoadService是上传实现类,这里暂时不暂时源码。

可是,在访问该接口的时候,报错了:org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported

org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:226)
    at org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.resolveArgument(RequestPartMethodArgumentResolver.java:134)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:131)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:891)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:981)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:884)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:858)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)

错误分析

org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported,意思是应用不支持'application/octet-stream'这种文件类型

Swagger2里的请求参数:

 

 分析异常堆栈,是在AbstractMessageConverterMethodArgumentResolver的readWithMessageConverters方法里抛出的:


         分析这个方法里的代码,发现Spring在尝试使用各个消息转换器(HttpMessageConverter)来处理请求。这里的contentType值是'application/octet-stream',发现找不到可用的消息转换器:

genericConverter.canRead(targetType, contextClass, contentType) ,因此无法解析、转换消息,因此抛出了异常。

         确切的说,上面说的是处理请求参数uploadFileDto,第一个请求参数files已经在下面这个地方处理了(文件类型参数使用MultipartResolutionDelegate.resolveMultipartArgument来处理):

注:

messageConverters的值(这个看各个应用里注册的消息转换器类型、顺序):

经常使用的是MappingJackson2HttpMessageConverter消息转换器,这个是SpringBoot默认用来处理消息的,将原始请求转成json格式,再转成目标类型的格式,是Jackson

targetClass是UploadFileDto对应的Class

解决方法

        明白了问题产生的原因之后,就有了解决思路:增加一个消息转换器,用于支持'application/octet-stream'。

最简单的添加方式,新增一个类,继承AbstractJackson2HttpMessageConverter,这个也是上面说的MappingJackson2HttpMessageConverter的父类

@Component
public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {

    /**
     * Converter for support http request with header Content-Type: multipart/form-data
     */
    public MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
    }

    @Override
    public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
        return false;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return false;
    }

    @Override
    protected boolean canWrite(MediaType mediaType) {
        return false;
    }
}

 加了上面的转换器之后:

 读取之后的body类型就是UploadFileDto

 至此,问题解决,后端程序能够正常接收、处理前端发过来的文件列表,以及复杂参数。

简单参数

如果后端程序不需要接收复杂参数,而是只需要固定的几个简单参数,那么就很简单,比如:

Controller:


    @PostMapping(value = "/upload2")
    @ApiOperation("文件上传2")
    public ResponseDto<String> uploadFile(@RequestParam("files") MultipartFile[] files,  @RequestPart() String remark) {
        System.out.println("remark = " + remark);
        upDownLoadService.uploadFile(remark, files);
        return getResponseDto(remark);
    }

此时,contentType值是'application/octet-stream'的String的参数,Spring有提供了默认的消息转换器StringHttpMessageConverter,支持所有的格式,就不需要自定义MappingJackson2HttpMessageConverter消息转换器了。

总结

回顾一下,如果需要复杂参数(自定义对象接收前端参数),那么需要自定义消息转换器(如AbstractJackson2HttpMessageConverter),来支持contentType值是'application/octet-stream'类型的参数,并将其转换成目标格式;

如果不需要复杂参数,只是String等类型,那么不需要自定义消息转换器;

消息转换器是SpringBoot处理前端传输的数据,并转换成接口参数的类型的转换器,转换前、转换后还支持自定义插件处理。详见AbstractMessageConverterMethodArgumentResolver的readWithMessageConverters

@RequestParam和@RequestPart的区别,可以自行查找资料

参考:

 rest - @RequestPart with mixed multipart request, Spring MVC 3.2 - Stack Overflow

@RequestPart同时接收文件和json后端报错 - 简书 (jianshu.io)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/98682.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言——操作符详解(上)

C语言——操作符详解&#xff08;上&#xff09; 操作符的分类 C语言中的操作符主要分为算术操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式、下标引用、函数调用和结构成员。我将分成三篇文章为大家详细介绍以上所…

[附源码]Python计算机毕业设计Django网约车智能接单规划小程序

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

[附源码]Python计算机毕业设计华夏商场红酒管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等…

AI绘画火爆,以昆仑万维AIGC为例,揭秘AI绘画背后的模型算法

AI绘画火爆&#xff0c;以昆仑万维AIGC为例&#xff0c;揭秘AI绘画背后的模型算法 一、前言 最近AI绘画让人工智能再次走进大众视野。在人工智能发展早起&#xff0c;一直认为人工智能能实现的功能非常有限。通常都是些死板的东西&#xff0c;像是下棋、问答之类的&#xff0…

mysql锁范围(一)表级锁变行级锁

文章目录行级锁1. 用两个连接connection登陆mysql2. 测试无索引情况1&#xff09;机器1开启事务&#xff0c;执行更新北京仓数据sql&#xff0c;不提交事务2&#xff09;机器2开启事务&#xff0c;先查询北京仓3&#xff09;机器2开始更新上海仓数据4&#xff09;机器1事务回滚…

【Spring Cloud】Nacos服务分级存储模型与负载均衡原理与实战

本期目录1. 服务分级模型介绍2. 服务分级模型的必要性3. 配置集群属性4. NacosRule负载均衡4.1 背景描述4.2 配置Nacos负载均衡策略4.3 根据权重负载均衡1. 服务分级模型介绍 为了提升整个系统的容灾性&#xff0c;Nacos 引入了地域 (Zone) 的概念&#xff0c;如上图中的北京、…

Reactor 和 Proactor 区别

Reactor 和 Proactor 区别 同步异步、阻塞非阻塞组合 同步 以read()函数为例&#xff0c;int n read(fd, buf. sz) 当采用同步的方式和阻塞io的方式时&#xff0c;buf就是从内核拷贝的数据&#xff0c;函数返回则可以马上知道 buf 中的数据。当采用同步的方式和非阻塞io的方式…

关于rabbitmq消息推送的小demo

目录 一.前言 1.1场景 1.2消息交换机三种形式 二.建设demo工程 2.1 依赖 2.2yml文件指定rabbitmq连接信息 2.3直连型消息链接 一.前言 1.1场景 在我们实际开发中到一个特定的时候是比如工作流到某个状态时, 我们会向某某单位发送消息, 这时就会用到我们的消息推送---ra…

javaee之Mybatis2

一、保存操作 在做这个方法之前&#xff0c;我们先把之前做的那个MybatisTest里面的每一个方法做成一个Test方法&#xff0c;也就是标注Test这个注解 这样便于我们测试接下来的每一个方法。仔细分析一下上面的代码&#xff0c;会发现&#xff0c;可重复性的地方太多。比如我们…

两台linux服务器rsync自动备份文件

检查rsycn是否安装 检查方法&#xff1a;rpm -qa rsync 出现rsync 包名就是安装了 安装rsycn rsync的安装可以使用yum直接安装&#xff1a;yum install rsync rsycn的服务端/文件接收端配置 1、先创建备份目录 mkdir /data/xsbak2、服务端需要开启rsyncd服务&#xff0c;添加…

接口测试(九)—— Git代码托管、jenkins 的持续集成

目录 一、持续集成 二、git 1、简介和安装 2、Gitee 2.1 git 和 gitee 管理代码工作原理 2.2 PyCharm 配置 Gitee 插件 3、PyCharm 与 Gitee 相关操作 3.1 将 Gitee的项目 Checkout到 Pycharm中 3.2 推送 PyCharm 新项目到 Gitee远程仓库 3.3 将 Pycharm代码 push到 …

React基础知识(组件实例三大核心属性state、props、refs)(二)

系列文章目录 第一章&#xff1a;React基础知识&#xff08;React基本使用、JSX语法、React模块化与组件化&#xff09;&#xff08;一&#xff09; 文章目录系列文章目录一、State1.1. state基本使用1.2 state的简写形式二、Props2.1 props的基本使用2.2 props属性值限制2.3 …

精品基于SSM的小学生课程资源网络云平台

《基于SSM的小学生课程资源网络云平台》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 使用技术&#xff1a; 开发语言&#xff1a;Java 框架&#xff1a;ssm 技术&#xff1a;JSP JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据…

redux

文章目录redux是什么redux使用情况redux工作原理redux安装redux的3个核心概念Action——形成动作Reducers——操作状态store——存储状态APIredux的响应式redux实现加减案例不使用ActionCreators实现使用ActionCreators实现异步Actionreact-redux使用容器组件创建修改App.jsx在…

干电池升压IC3.3V的电源芯片fs2111/FS2112

干电池升压3.3V的电源芯片FS2111/FS2112 FS2111/FS2112适用于一节干电池升压到3.3V&#xff0c;两节干电池升压3.3V的升压电路&#xff0c;FS2111/FS2112干电池升压IC。 FS2111/FS2112 干电池1.5V和两节干电池3V升压到3.3V的测试数据 两节干电池输出500MA测试&#xff1a; F…

CubeMax添加Rtthread操作系统 组件STM32F103

CubeMax添加Rtthread操作系统 组件STM32F103 本篇主要介绍&#xff0c;怎么使用STM32CubeMx工具&#xff0c;添加RT-Thread操作系统组件&#xff0c;码代码的IDE是keil。快速移植RT-Thread实时操作系统&#xff0c;所用的IDE可自行官网下载最新版。 CubeMax官网下载链接 RTthre…

Ajax(六)

1. XMLHttpRequest的基本使用——URL编码与解码 1.1 什么是URL编码 1.2 如何对URL进行编码与解码 <body><script>var str 黑马程序员//对str编码var str2 encodeURI(str)console.log(str2)//一个汉字对应三个百分号&#xff0c;反解码从console里头复制console.…

js解决单线程之路 - worker的使用分析

写在前面 今天写一个关于实现多线程的东西&#xff0c;都知道js是一个单线程的语言&#xff0c;所谓的单线程就是一次只能做一件事&#xff0c;多线程就是一次可以做很多件事&#xff0c;当然目前我们的电脑等设备很少会有单线程了&#xff0c;比如我们的电脑一般都是标的6核12…

stm32f103zet6的GPIO基础知识

IO数量 16*7112个&#xff0c;GPIOA~GPIOG7组,共144个引脚 IO模式 很多IO口既可以做为输入&#xff0c;也可以做为输出 输入模式 VSS指的是地&#xff0c;VDD是高电平&#xff0c; MOS英文全称为Metal-Oxide-Semiconductor。 意思为金属-氧化物-半导体&#xff0c;而拥有这…

Python画一棵茂盛的分形树

文章目录前情回顾添加分岔茂盛的分形树前情回顾 上次画了一棵分形树&#xff1a;用Python画一棵分形树&#xff0c;得到的图如下 发现看的人还是挺多的&#xff0c;但没什么人点赞&#xff0c;这说明我能给大家画分形树&#xff0c;大家很高兴&#xff0c;但这棵树太秃了&…