SpringMVC返回图片的几种方式

后端提供服务,通常返回的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 Map map;

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 Map map;

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中,当然实际和上面有一些不同,毕竟与业务相关较大,有兴趣的可以参考

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

IV. 其他

声明

尽信书则不如,已上内容,纯属一家之言,因本人能力一般,见解不全,如有问题,欢迎批评指正

扫描关注,java分享

java servlet 返回图片_SpringMVC返回图片的几种方式相关推荐

  1. 实现图片预加载的几种方式

    感觉自己好久没有写博客了,可能自己变懒了.不知道为什么最近有点迷茫,不知道是该去学一下新东西还是该去看一下具有深度的东西.新的技术需要关注,但是我要去研究一下jquery的源码,这个东西很早就想去看, ...

  2. python怎么下载图片怎么保存到本地_详解Python下载图片并保存本地的两种方式

    一:使用Python中的urllib类中的urlretrieve()函数,直接从网上下载资源到本地,具体代码: import os,stat import urllib.request img_url ...

  3. python 图片和二进制转换的三种方式

    PIL格式转二进制 先读取为PIL格式,再转为二进制 import io import base64 from PIL import Imagedef image2byte(image):'''图片转 ...

  4. 原生态的ajax如何上传文件,原生ajax和iframe框架实现图片文件上传的两种方式

    大家应该可以举出几种常用的异步文件上传功能的实现方式,使用频率较多的有原生ajax和iframe框架,实现图片文件上传,下面就为大家分享图片文件上传的两种方式:原生ajax和iframe框架,供大家参 ...

  5. java 生成二维码 QRCode、zxing 两种方式

    版权声明:本文为 testcs_dn(微wx笑) 原创文章,非商用自由转载-保持署名-注明出处,谢谢. https://blog.csdn.net/testcs_dn/article/details/ ...

  6. java 读取css文件_java文件读取的两种方式

    JAVA中读取文件(二进制,字符)内容的几种方 JAVA中读取文件内容的方法有很多,比如按字节读取文件内容,按字符读取文件内容,按行读取文件内容,随机读取文件内容等方法,本文就以上方法的具体实现给出代 ...

  7. java反射之获取class对象,Java之反射机制(获取Class对象的三种方式)

    Java之反射机制(获取Class对象的三种方式) 开发工具与关键技术:MyEclipse 10,java 作者:刘东标 撰写时间:2019-06-14 如何得到各个字节码对应的实例对象? 每个类被加 ...

  8. JS基础-Java Class类以及获取Class实例的三种方式

    JS基础-Java Class类以及获取Class实例的三种方式 由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名.包名.父类.实现的接口. ...

  9. Java中创建(实例化)对象的五种方式

    Java中创建(实例化)对象的五种方式 1.用new语句创建对象,这是最常见的创建对象的方法. 2.通过工厂方法返回对象,如:String str = String.valueOf(23);  3.运 ...

  10. 19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

最新文章

  1. Shell 开发在运维中的经验总结
  2. 机器学习笔记(八)集成学习
  3. unittest单元测试框架总结
  4. JAVA连接数据库ij_Derby 客户端 ij使用
  5. hdu 2444(二分图的判断以及求最大匹配)
  6. C#:适配器设计模式如何让您的生活更轻松
  7. app运行租用服务器,app租用服务器
  8. 2015-11-16
  9. vscode 连接服务器jupyter_VScode中使用jupyter notebook
  10. windows docker 设置镜像源_Windows10下搭建第一个Docker应用(Demo)
  11. Docker操作命令——查看、停止、删除容器
  12. 浅谈浏览器端JavaScript跨域解决方法
  13. 数字的格式化c语言课程设计,【图片】发几个C语言课程设计源代码(恭喜自己当上技术小吧主)【东华理工大学吧】_百度贴吧...
  14. 还债之期末复习(预习)
  15. 达梦数据库工程师培训实战教程(主备集群、读写分离、共享集群)
  16. JAVA使用JCo连接SAP介绍-1
  17. 浏览器网页无法打开麦克风、摄像头
  18. 小程序提示 网络异常 Network Anomalies
  19. android qq红点,手机QQ的拖动红点消除红点功能是怎么想出来的?
  20. 不同坐标系下角速度_轨道、重力场与坐标系的混乱关系

热门文章

  1. 8-12-COMPETITION
  2. c++图书管理系统_轻松学做C语言课程设计:图书管理系统-数组实现
  3. 零基础开始学 Web 前端开发,有什么建议吗?--知
  4. learnpython有中文版吗_简介 | Learn Python the Hard Way 中文版
  5. 3.8 Softmax 回归-深度学习第二课《改善深层神经网络》-Stanford吴恩达教授
  6. Android Studio错误代码汇总
  7. SLAM-ch2-cmake中使用库
  8. 【DIY】不到20元,升级热水器加装远程wifi控制功能,esp8266远程红外控制热水器启动...
  9. Vivado联合ModelSim仿真设置(附图步骤)
  10. 管理和安装 chart - 每天5分钟玩转 Docker 容器技术(168)