前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >补习系列-springboot mime类型处理

补习系列-springboot mime类型处理

作者头像
美码师
发布于 2018-08-27 08:25:02
发布于 2018-08-27 08:25:02
2.2K00
代码可运行
举报
文章被收录于专栏:美码师美码师
运行总次数:0
代码可运行

目标

  1. 了解http常见的mime类型定义;
  2. 如何使用springboot 处理json请求及响应;
  3. 如何使用springboot 处理 xml请求及响应;
  4. http参数的获取及文件上传下载;
  5. 如何获得原始请求的字节流; 6.了解springboot 如何实现内容转换;

一、关于MIME

MIME的全称是Multipurpose Internet Mail Extensions,即多用途互联网邮件扩展,尽管读起来有些拗口,但大多数人可能都知道, 这是HTTP协议中用来定义文档性质及格式的标准。IETF RFC 6838,对HTTP传输内容类型进行了全面定义。 而 IANA(互联网号码分配机构)是负责管理所有标准MIME类型的官方机构。可以在这里)找到所有的标准MIME

服务器通过MIME告知响应内容类型,而浏览器则通过MIME类型来确定如何处理文档; 因此为传输内容(文档、图片等)设置正确的MIME非常重要。 通常Server会在HTTP响应中设置Content-Type,如下面的响应:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
HTTP/1.1 200 OK
Server: Golfe2    
Content-Length: 233
Content-Type: application/html
Date: Sun, 28 Dec 2018 02:55:19 GMT

这表示服务端将返回html格式的文档,而同样客户端也可以在HTTP请求中设置Content-Type以告知服务器当前所发送内容的格式。

如下面的请求体:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
POST / HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Connection: keep-alive
Content-Type: application/json
Content-Length: 465

这表示客户端会发送application/json格式的数据到服务端,同时应该注意到Accept请求头,这个选项用于告知服务器应该返回什么样的数据格式(由客户端接收并完成解析)。

MIME的格式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type/subtype

这是一个两级的分类,比较容易理解,第一级分类通常包含:

类型

描述

text

普通文本

image

某种图像

audio

某种音频文件

video

某种视频文件

application

应用数据

multi-part

复合内容

而二级类型则非常多,以下是一些常用的MIME:

MIME

描述

audio/wav

wave音频流媒体文件

audio/webm

webm 音频文件格式

audio/ogg

ogg多媒体文件格式的音频文件

audio/mpeg

mpeg多媒体文件格式的音频文件

image/gif

gif图片

image/jpeg

jpeg图片

image/png

png图片

image/svg+xml

svg矢量图片

application/json

json格式

application/xml

xml格式

application/xhtml+xml

扩展html格式

application/x-www-form-urlencoded

表单url内容编码

application/octet-stream

二进制格式

application/pdf

pdf文档

application/atom+xml

atom订阅feed流

multipart/form-data

多文档格式

text/plain

普通文本

text/html

html文档

text/css

css文件

text/javascript

javascript文件

text/markdown

markdown文档

video/mpeg

mpeg多媒体视频文件

video/quicktime

mov多媒体视频文件

接下来,看看springboot如何实现几个常见类型格式的处理。

二、springboot-json处理

先看看这样一段代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@ResponseBody
    @PostMapping(value = "/json", consumes= { MediaType.APPLICATION_JSON_UTF8_VALUE }, produces="application/json;charset=UTF-8")
    public Map<String, Object> jsonIO(@RequestBody Map<String, Object> jsonData) {
        Map<String, Object> resultData = new HashMap<>(jsonData);
        resultData.put("resultCode", UUID.randomUUID().toString());
        return resultData;
    }

这是一个Controller层的方法定义,其中@PostMapping将该方法映射到/json路径的POST方法。

  1. consumes = { MediaType.APPLICATIONJSONUTF8_VALUE } 指定了该方法仅处理application/json的内容格式
  2. produces="application/json;charset=UTF-8" 则表示会在响应头中指定Content-Type=application/json;charset=UTF-8
  3. @RequestBody 指定了将请求的输入通过Json转换为DTO
  4. @ResponseBody 指定将响应对象转换为Json格式输出

通过观察请求响应,我们会得到以下的结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
====> Request:
Content-Type=application/json;
{
    "key": "value"
}
====> Response: 
Content-Type=application/json;charset=UTF-8
{
    "resultCode": "1ec407e1-d753-4439-b31c-bb7e888aa6a2",
    "key": "value"
}

使用Postman工具进行调试,可以非常直观的获得想要的信息,点击这里可以下载

异常情况

如果,请求的内容格式不是json,而是其他的如application/x-www-form-urlencoded呢? 放心,框架会返回如下面的错误:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
    "timestamp": 1530626924715,
    "status": 415,
    "error": "Unsupported Media Type",
    "exception": "org.springframework.web.HttpMediaTypeNotSupportedException",
    "message": "Content type 'application/x-www-form-urlencoded' not supported",
    "path": "/content/json"
}

三、springboot-xml处理

如上,通过springboot框架,我们快速实现了Json格式的输入输出。 那么,如何实现xml格式的处理呢?xml格式主要用于soap、rpc等领域,为了实现xml数据的序列化,我们需要添加jackson-xml依赖包

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!-- support for xml bean -->
    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-xml</artifactId>
        <version>2.8.6</version>
    </dependency>

接下来,声明一个Controller方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@PostMapping(value = "/xml", consumes = {
            MediaType.APPLICATION_XML_VALUE }, produces = MediaType.APPLICATION_XML_VALUE)
    @ResponseBody
    public ParamData xmlIO(@RequestBody ParamData data) {
        data.setAge(data.getAge() + 1);
        return data;
    }

这次,我们指定了consumes、produces都是application/xml,通过@RequestBody、@ResponseBody注解之后, springboot框架会自动根据需求的内容格式进行转换。

这里的ParamData是一个简单的Pojo类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static class ParamData {

        private String name;
        private int age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

    }

通过真实的请求-响应观测,我们得到如下的结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
====> Request:
Content-Type=application/xml;
<ParamData>
  <name>Jim</name>
  <age>1</age>
</ParamData>
====> Response: 
Content-Type=application/xml;charset=UTF-8
<ParamData>
    <name>Jim</name>
    <age>2</age>
</ParamData>

BTW,springboot 完成自动类型转换是通过内容协商实现的,相关的接口为ContentNegotiationManager。 默认情况下,对于声明了consumes及produce属性的方法,会按照声明的值进行处理,否则格式的转换会根据请求中的Content-Type、Accept头部来进行判断。

此外,实现请求/响应内容到DTO转换功能的是HttpMessageConverter接口。

准确说,内容转换是由springmvc框架提供,而springboot是一个整合模块的脚手架

四、http参数处理

对于普通的表单请求参数处理,我们通常有两种方式:

  • 通过方法参数映射
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@PostMapping(value = "/form", consumes = {
            MediaType.APPLICATION_FORM_URLENCODED_VALUE }, produces = MediaType.TEXT_PLAIN_VALUE)
    @ResponseBody
    public String form(@RequestParam("name") String name, @RequestParam("age") int age) {
        return String.format("Welcome %s, you are %d years old", name, age);
    }
  • 通过参数绑定
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@PostMapping(value = "/form1", consumes = {
            MediaType.APPLICATION_FORM_URLENCODED_VALUE }, produces = MediaType.TEXT_PLAIN_VALUE)
    @ResponseBody
    public String form1(ParamData data) {
        return String.format("Welcome %s, you are %d years old. Bye", data.getName(), data.getAge());
    }

form表单的请求内容格式为application/x-www-form-urlencoded,

一个请求的样例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
====>Request:
Content-Length →40
Content-Type →text/plain;charset=UTF-8
Date →Mon, 16 Jul 2018 13:50:14 GMT

name=Lilei
age=11

====>Response:
Content-Length →40
Content-Type →text/plain;charset=UTF-8
Date →Mon, 16 Jul 2018 13:50:14 GMT

Welcome Lilei, you are 11 years old. Bye

五、文件上传下载

对于文件上传,我们需要将请求声明为multipart/form-data格式,一个文件上传的请求样例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
POST / HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------8721656041911415653955004498
Content-Length: 465

-----------------------------8721656041911415653955004498
Content-Disposition: form-data; name="name"

Test
-----------------------------8721656041911415653955004498
Content-Disposition: form-data; name="file"; filename="flower.jpg"
Content-Type: image/jpeg

....
-----------------------------8721656041911415653955004498--

参照以下的代码可以实现简单的文件上传处理:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@PostMapping(value = "file", consumes = {
            MediaType.MULTIPART_FORM_DATA_VALUE }, produces = MediaType.TEXT_PLAIN_VALUE)
    @ResponseBody
    public String file(@RequestParam("name") String name, @RequestParam("file") MultipartFile file) {

        logger.info("file receive {}", name);

        if (file.isEmpty()) {
            return "No File";
        }
        String fileName = file.getOriginalFilename();

        File root = new File("D:/temp");
        if (!root.isDirectory()) {
            root.mkdirs();
        }
        try {
            file.transferTo(new File(root, name));
            return String.format("Upload to %s", fileName);
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "Upload Failed";
    }

这个例子非常简单,通过声明@RequestParam注解获得MultipartFile 对象,在获得上传文件后存储到服务器本地目录。 当然,在真实的项目应用中你需要做的更多,比如文件的大小、类型校验,将文件进行压缩或将文件存放到大容量、高稳定性的分布式文件存储系统等等。

这里不多啰嗦了,关于文件下载,可以通过以下的方法实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@GetMapping(path = "/download")
    public ResponseEntity<Resource> download(@RequestParam("name") String name) throws IOException {

        File file = new File("D:/temp", name);
        Path path = Paths.get(file.getAbsolutePath());
        ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(path));
        return ResponseEntity.ok().header("Content-Disposition", "attachment;fileName=" + name)
                .contentLength(file.length()).contentType(MediaType.APPLICATION_OCTET_STREAM).body(resource);
    }

聪明的读者一定会发现,除了将文件内容作为输出之外,我们还为响应添加两个header:

  1. Content-Type:application/octet-stream,这表示响应的文档是未知的二进制数据,大多数情况下浏览器会直接下载;
  2. Content-Disposition →attachment;fileName=test.jpg,表示文档应该作为附件保存,并名称为test.jpg。

六、获得原始字节流

在某些情况下,你可能需要获得原始的请求字节流,比如实现内容的过滤,或者为了完成制作自己的RPC接口。 在springboot中获得字节流非常简单,从Servlet API的定义中可以发现,直接通过HttpServletRequest对象便可以获取一个InputStream。

在我们定义的Controller方法中,还可以直接声明流类型的参数以获取数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@PostMapping(value = "/data", produces = MediaType.TEXT_PLAIN_VALUE)
    @ResponseBody
    public String rawIO(InputStream dataStream) throws Exception {
        return IOUtils.toString(dataStream, "UTF-8");
    }

然而,如果这么做了,你可能会遇到一些麻烦: 当请求头中Content-Type=application/x-www-form-urlencoded 时,你会获得一个空的InputStream!

笔者曾经在制作代理服务器的时候遇到了这个问题,经过一番查阅,发现问题的原因在于:

按照Servlet规范,如果同时满足下列条件,则请求体(Entity)中的表单数据,将被填充到request的parameter集合中(导致inputstream为空)。 1 这是一个HTTP/HTTPS请求 2 请求方法是POST 3 请求的类型Content-Type=application/x-www-form-urlencoded 4 Servlet调用了getParameter系列方法

springboot框架内置了HiddenHttpMethodFilter,用于支持浏览器form表单无法支持put/delete等请求方法的问题。 在Filter的实现中发现存在如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
            String paramValue = request.getParameter(this.methodParam);
            if (StringUtils.hasLength(paramValue)) {
                requestToUse = new HttpMethodRequestWrapper(request, paramValue);
            }
        }

由于getParameter被提前调用,导致后续获取InputStream为空。 该问题的解决方法是实现HttpServletRequest的代理,事先将InputStream保存起来供多次使用,通过高优先级的过滤器提前将Request对象置换可达到目的。 由于篇幅限制这里不做展开。感兴趣的可以参考这里获得更多信息。

小结

HTTP协议中定义了MIME标准,以实现传输内容格式的识别及转换。 本文介绍了常见的MIME类型,并结合springboot框架的代码样例,讲述如何完成Json/xml/字节流等常见类型的内容处理。

对于Http参数、文件的上传下载提供了简单代码示例,读者在充分了解用法之后可以进一步完善,并应用到实际的项目中去。

最后,欢迎继续关注"美码师的补习系列-springboot篇" ,期待更多精彩内容^-^

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 美码师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Github高赞的YOLOv5引发争议?Roboflow和开发者这样说
YOLOv5自发布之后就受到了许多关注,无论是Hacker News,Github还是Reddit,在各个机器学习有关的平台上都引发了广泛的讨论。当然,也不少用户对YOLOv5提出了各方面的质疑。
新智元
2020/06/17
1.2K0
Github高赞的YOLOv5引发争议?Roboflow和开发者这样说
YOLOv5它来了!YOLOv4发布不到50天,它带着推理速度140帧/秒、性能提升2倍来了
6月9日,Ultralytics公司开源了YOLOv5,离上一次YOLOv4发布不到50天。而且这一次的YOLOv5是完全基于PyTorch实现的!
量子位
2020/06/16
1.8K0
大神接棒,YOLOv4来了!
之前,YOLO系列(v1-v3)作者 Joe Redmon 宣布不再继续CV方向的研究,引起学术圈一篇哗然。YOLO之父宣布退出CV界,坦言无法忽视自己工作带来的负面影响
Amusi
2020/06/30
1.1K0
大神接棒,YOLOv4来了!
杀疯了!YOLO再突破,提速20倍!!
YOLO再一次突破,新变体YOLO-World在目标检测领域的表现非常的出色。开集检测速度提升20倍!
Python编程爱好者
2024/04/12
1K0
杀疯了!YOLO再突破,提速20倍!!
使用Yolov5进行端到端目标检测
最近,Ultralytics推出了YOLOv5,但它的名字却引发了争议。为了了解背景,《YOLO》(你只能看一次)的前三个版本是由约瑟夫·雷蒙(Joseph Redmon)创作的。在此之后,Alexey Bochkovskiy在darknet上创建了YOLOv4,号称比之前的迭代具有更高的平均精度(AP)和更快的结果。
deephub
2020/07/21
1.7K0
使用Yolov5进行端到端目标检测
PP-YOLO何许模型?竟然超越了YOLOv4
PP-YOLO评估指标显示出比现有的最新对象检测模型YOLOv4更高的性能。但是,提出者百度却谦虚的声明:
小白学视觉
2020/08/13
1.2K0
YOLO算法最全综述:从YOLOv1到YOLOv5
来源丨https://zhuanlan.zhihu.com/p/136382095
Datawhale
2020/10/23
2.6K0
YOLO算法最全综述:从YOLOv1到YOLOv5
如何使用Yolov5创建端到端对象检测器?
Ultralytics最近在围绕其名称的争议中推出了YOLOv5。就上下文而言,约瑟夫·雷德蒙(Joseph Redmon)创建了YOLO(您只看一次)的前三个版本。此后,Alexey Bochkovskiy在Darknet上创建了YOLOv4,与以前的迭代相比,它拥有更高的平均精度(AP)和更快的结果。
代码医生工作室
2020/07/13
1.4K0
深入浅出Yolo系列之Yolov3&Yolov4&Yolov5&Yolox核心基础知识完整讲解
因为工作原因,项目中经常遇到目标检测的任务,因此对目标检测算法会经常使用和关注,比如Yolov3、Yolov4算法、Yolov5算法、Yolox算法。
全栈程序员站长
2022/07/01
7300
深入浅出Yolo系列之Yolov3&Yolov4&Yolov5&Yolox核心基础知识完整讲解
知识精讲 | Yolov3和Yolov4核心内容、代码梳理
从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗。
新智元
2020/06/09
1.8K0
知识精讲 | Yolov3和Yolov4核心内容、代码梳理
名声大噪的YOLO迎来YOLOv8,迅速包揽目标检测、实例分割新SOTA
机器之心报道 编辑:小舟、陈萍 YOLOv8 抛弃了前几代模型的 Anchor-Base。 YOLO 是一种基于图像全局信息进行预测的目标检测系统。自 2015 年 Joseph Redmon、Ali Farhadi 等人提出初代模型以来,领域内的研究者们已经对 YOLO 进行了多次更新迭代,模型性能越来越强大。现在,YOLOv8 已正式发布。 YOLOv8 是由小型初创公司 Ultralytics 创建并维护的,值得注意的是 YOLOv5 也是由该公司创建的。 YOLOv8 项目地址:https://g
机器之心
2023/03/29
7.4K0
名声大噪的YOLO迎来YOLOv8,迅速包揽目标检测、实例分割新SOTA
YOLOv8来啦!YOLO内卷期模型怎么选?9+款AI硬件如何快速部署?深度解析
此外换模型训练调参也会引入更多的不确定性,而且往往业务数据集大则几十万张图片,重训成本很高,但训完了新的精度不一定更高,速度指标在特定机器环境上也未必可观,参数量、计算量的变化尤其在边缘设备上也不能忽视。
用户1386409
2023/03/06
1.3K0
YOLOv8来啦!YOLO内卷期模型怎么选?9+款AI硬件如何快速部署?深度解析
【目标检测】从YOLOv1到YOLOX(理论梳理)
YOLO系列应该是目标领域知名度最高的算法,其凭借出色的实时检测性能在不同的领域均有广泛应用。 目前,YOLO共有6个版本,YOLOv1-v5和YOLOX,除了YOLOv5外,其它都有相应的论文,5篇论文我已上传到资源中,可自行下载:https://www.aliyundrive.com/s/ofcnrxjzsFE 工程上使用最多的版本是YOLOv3和YOLOv5,Pytorch版本均由ultralytics公司开发,YOLOv5仍在进行维护,截至目前,已经更新到YOLOv5-6.1版本。 项目地址:https://github.com/ultralytics/yolov5 在上篇博文中,详细记录了如何用YOLOv5来跑通VOC2007数据集,本篇博文旨在对YOLO系列算法的演化进行简单梳理,更多详细的内容可以看文末的参考资料。
zstar
2022/09/08
2.2K0
YOLO 系目标检测算法家族全景图!
YOLO目标检测算法诞生于2015年6月,从出生的那一天起就是“高精度、高效率、高实用性”目标检测算法的代名词。
CV君
2020/08/04
1.7K0
YOLO 系目标检测算法家族全景图!
YOLOv8已至,精度大涨!教你如何在自定义数据集上训练它
丰色 发自 凹非寺 量子位 | 公众号 QbitAI 很快啊—— 目标检测经典模型YOLO的第八个版本就已经发布了。 这次升级不少,包括命令行界面、Python API、backbone结构等,精度相比YOLOv5高了一大截(速度官方还没公布)。 下面是网友实测,几个不同规模的变体在目标检测、实例分割和图像分类三项任务上的涨点最高达到了33.21%。 不知道YOLOv8这一出,v5版本还会“苟”多久? oh我们还发现已经有人用它在自定义数据集上完成了一波训练,效果是这样滴: 这精准度和稳定性,让网友狠
量子位
2023/02/28
4K0
YOLOv8已至,精度大涨!教你如何在自定义数据集上训练它
当YOLOv5遇见OpenVINO!
YOLOv5 于2020年6月发布!一经推出,便得到CV圈的瞩目,目前在各大目标检测竞赛、落地实战项目中得到广泛应用。
AI科技评论
2021/07/03
2.6K0
当YOLOv5遇见OpenVINO!
YOLO进化史:YOLOv5、YOLOv8 与 YOLOv10 的性能分析与边缘部署探讨!
AI模型部署落地实战👉「CUDA、TensorRT、NCNN、OpenVINO、MNN、ONNXRuntime以及地平线框架」
集智书童公众号
2024/07/11
18.1K0
YOLO进化史:YOLOv5、YOLOv8 与 YOLOv10 的性能分析与边缘部署探讨!
YOLO项目复活!大神接过衣钵,YOLO之父隐退2月后,v4版正式发布,性能大幅提升
另一位曾经参与YOLO项目维护的大神Alexey Bochkovskiy,在arXiv上提交了YOLO v4,而且这篇论文已经被拉入原来YOLO之父建立的项目主线。
量子位
2020/04/24
8180
YOLO项目复活!大神接过衣钵,YOLO之父隐退2月后,v4版正式发布,性能大幅提升
YOLO家族系列模型的演变:从v1到v8(下)
昨天的文章中,我们回顾了 YOLO 家族的前 9 个架构。本文中将继续总结最后3个框架,还有本月最新发布的YOLO V8.
deephub
2023/02/01
2.8K0
YOLO v4它来了:接棒者出现,速度效果双提升
两个月前,YOLO 之父 Joseph Redmon 表示,由于无法忍受自己工作所带来的的负面影响,决定退出计算机视觉领域。此事引发了极大的热议,其中一个悬念就是:我们还能等到 YOLO v4 面世吗?
机器之心
2020/04/26
1.1K0
推荐阅读
相关推荐
Github高赞的YOLOv5引发争议?Roboflow和开发者这样说
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文