JAVA语言之SpringMVC返回图片的几种方式(小结)[Java代码]
龚超 2018-07-20 来源 : 阅读 1748 评论 0

摘要:本文主要向大家介绍了JAVA语言的SpringMVC返回图片的几种方式,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了JAVA语言的SpringMVC返回图片的几种方式,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

后端提供服务,通常返回的json串,但是某些场景下可能需要直接返回二进制流,如一个图片编辑接口,希望直接将图片流返回给前端,此时可以怎么处理?

I. 返回二进制图片

主要借助的是 HttpServletResponse这个对象,实现case如下


@RequestMapping(value = {"/img/render"}, method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS})

@CrossOrigin(origins = "*")

@ResponseBody

public String execute(HttpServletRequest httpServletRequest,

HttpServletResponse httpServletResponse) {

// img为图片的二进制流

byte[] img = xxx;

httpServletResponse.setContentType("image/png");

OutputStream os = httpServletResponse.getOutputStream();

os.write(img);

os.flush();

os.close();

return "success";

}


复制代码

注意事项

注意ContentType定义了图片类型

将二进制写入 httpServletResponse#getOutputStream

写完之后,flush(), close()请务必执行一次

II. 返回图片的几种方式封装

一般来说,一个后端提供的服务接口,往往是返回json数据的居多,前面提到了直接返回图片的场景,那么常见的返回图片有哪些方式呢?

返回图片的http地址

返回base64格式的图片

直接返回二进制的图片

其他...(我就见过上面三种,别的还真不知道)

那么我们提供的一个Controller,应该如何同时支持上面这三种使用姿势呢?

1. bean定义

因为有几种不同的返回方式,至于该选择哪一个,当然是由前端来指定了,所以,可以定义一个请求参数的bean对象


@Data

public class BaseRequest {

private static final long serialVersionUID = 1146303518394712013L;

/**

* 输出图片方式:

*

* url : http地址 (默认方式)

* base64 : base64编码

* stream : 直接返回图片

*

*/

private String outType;

/**

* 返回图片的类型

* jpg | png | webp | gif

*/

private String mediaType;

public ReturnTypeEnum returnType() {

return ReturnTypeEnum.getEnum(outType);

}

public MediaTypeEnum mediaType() {

return MediaTypeEnum.getEnum(mediaType);

}

}


复制代码

为了简化判断,定义了两个注解,一个ReturnTypeEnum, 一个 MediaTypeEnum, 当然必要性不是特别大,下面是两者的定义


public enum ReturnTypeEnum {

URL("url"),

STREAM("stream"),

BASE64("base");


private String type;

ReturnTypeEnum(String type) {

this.type = type;

}

private static Mapmap;

static {

map = new HashMap<>(3);

for(ReturnTypeEnum e: ReturnTypeEnum.values()) {

map.put(e.type, e);

}

}


public static ReturnTypeEnum getEnum(String type) {

if (type == null) {

return URL;

}


ReturnTypeEnum e = map.get(type.toLowerCase());

return e == null ? URL : e;

}

}



复制代码


@Data

public enum MediaTypeEnum {

ImageJpg("jpg", "image/jpeg", "FFD8FF"),

ImageGif("gif", "image/gif", "47494638"),

ImagePng("png", "image/png", "89504E47"),

ImageWebp("webp", "image/webp", "52494646"),

private final String ext;

private final String mime;

private final String magic;

MediaTypeEnum(String ext, String mime, String magic) {

this.ext = ext;

this.mime = mime;

this.magic = magic;

}


private static Mapmap;

static {

map = new HashMap<>(4);

for (MediaTypeEnum e: values()) {

map.put(e.getExt(), e);

}

}


public static MediaTypeEnum getEnum(String type) {

if (type == null) {

return ImageJpg;

}

MediaTypeEnum e = map.get(type.toLowerCase());

return e == null ? ImageJpg : e;

}

}



复制代码

上面是请求参数封装的bean,返回当然也有一个对应的bean


@Data

public class BaseResponse {


/**

* 返回图片的相对路径

*/

private String path;



/**

* 返回图片的https格式

*/

private String url;



/**

* base64格式的图片

*/

private String base;

}



复制代码

说明:

实际的项目环境中,请求参数和返回肯定不会像上面这么简单,所以可以通过继承上面的bean或者自己定义对应的格式来实现

2. 返回的封装方式

既然目标明确,封装可算是这个里面最清晰的一个步骤了


protected void buildResponse(BaseRequest request,

BaseResponse response,

byte[] bytes) throws SelfError {

switch (request.returnType()) {

case URL:

upload(bytes, response);

break;

case BASE64:

base64(bytes, response);

break;

case STREAM:

stream(bytes, request);

}

}

private void upload(byte[] bytes, BaseResponse response) throws SelfError {

try {

// 上传到图片服务器,根据各自的实际情况进行替换

String path = UploadUtil.upload(bytes);


if (StringUtils.isBlank(path)) { // 上传失败

throw new InternalError(null);

}


response.setPath(path);

response.setUrl(CdnUtil.img(path));

} catch (IOException e) { // cdn异常

log.error("upload to cdn error! e:{}", e);

throw new CDNUploadError(e.getMessage());

}

}


// 返回base64

private void base64(byte[] bytes, BaseResponse response) {

String base = Base64.getEncoder().encodeToString(bytes);

response.setBase(base);

}

// 返回二进制图片

private void stream(byte[] bytes, BaseRequest request) throws SelfError {

try {

MediaTypeEnum mediaType = request.mediaType();

HttpServletResponse servletResponse = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();

servletResponse.setContentType(mediaType.getMime());

OutputStream os = servletResponse.getOutputStream();

os.write(bytes);

os.flush();

os.close();

} catch (Exception e) {

log.error("general return stream img error! req: {}, e:{}", request, e);

if (StringUtils.isNotBlank(e.getMessage())) {

throw new InternalError(e.getMessage());

} else {

throw new InternalError(null);

}

}

}


复制代码

说明:

请无视上面的几个自定义异常方式,需要使用时,完全可以干掉这些自定义异常即可;这里简单说一下,为什么会在实际项目中使用这种自定义异常的方式,主要是有以下几个优点

配合全局异常捕获(ControllerAdvie),使用起来非常方便简单

所有的异常集中处理,方便信息统计和报警

如,在统一的地方进行异常计数,然后超过某个阀值之后,报警给负责人,这样就不需要在每个出现异常case的地方来主动埋点了

避免错误状态码的层层传递

- 这个主要针对web服务,一般是在返回的json串中,会包含对应的错误状态码,错误信息
- 而异常case是可能出现在任何地方的,为了保持这个异常信息,要么将这些数据层层传递到controller;要么就是存在ThreadLocal中;显然这两种方式都没有抛异常的使用方便

有优点当然就有缺点了:

异常方式,额外的性能开销,所以在自定义异常中,我都覆盖了下面这个方法,不要完整的堆栈


@Override

public synchronized Throwable fillInStackTrace() {

return this;

}

复制代码

编码习惯问题,有些人可能就非常不喜欢这种使用方式

III. 项目相关

只说不练好像没什么意思,上面的这个设计,完全体现在了我一直维护的开源项目 Quick-Media中,当然实际和上面有一些不同,毕竟与业务相关较大,有兴趣的可以参考

QuickMedia: https://github.com/liuyueyi/quick-media :

BaseAction: com.hust.hui.quickmedia.web.wxapi.WxBaseAction#buildReturn

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @职坐标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论
本文作者 联系TA

擅长针对企业软件开发的产品设计及开发的细节与流程设计课程内容。座右铭:大道至简!

  • 370
    文章
  • 23309
    人气
  • 87%
    受欢迎度

已有23人表明态度,87%喜欢该老师!

进入TA的空间
求职秘籍 直通车
  • 索取资料 索取资料 索取资料
  • 答疑解惑 答疑解惑 答疑解惑
  • 技术交流 技术交流 技术交流
  • 职业测评 职业测评 职业测评
  • 面试技巧 面试技巧 面试技巧
  • 高薪秘笈 高薪秘笈 高薪秘笈
TA的其他文章 更多>>
WEB前端必须会的基本知识题目
经验技巧 93% 的用户喜欢
Java语言中四种遍历List的方法总结(推荐)
经验技巧 91% 的用户喜欢
Java语言之SHA-256加密的两种实现方法详解
经验技巧 75% 的用户喜欢
java语言实现把两个有序数组合并到一个数组的实例
经验技巧 75% 的用户喜欢
通过Java语言代码来创建view的方法
经验技巧 80% 的用户喜欢
其他海同师资 更多>>
吕益平
吕益平 联系TA
熟悉企业软件开发的产品设计及开发
孔庆琦
孔庆琦 联系TA
对MVC模式和三层架构有深入的研究
周鸣君
周鸣君 联系TA
擅长Hadoop/Spark大数据技术
范佺菁
范佺菁 联系TA
擅长Java语言,只有合理的安排和管理时间你才能做得更多,行得更远!
金延鑫
金延鑫 联系TA
擅长与学生或家长及时有效沟通
经验技巧30天热搜词 更多>>

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程