SpringMVC返回图⽚的⼏种⽅式(⼩结)
⽬录
I. 返回⼆进制图⽚
II. 返回图⽚的⼏种⽅式封装
1. bean定义
2. 返回的封装⽅式
III. 项⽬相关
后端提供服务,通常返回的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 = OutputStream();
os.write(img);
os.flush();
os.close();
return "success";
}
注意事项
1. 注意ContentType定义了图⽚类型
2. 将⼆进制写⼊ httpServletResponse#getOutputStream
3. 写完之后,flush(), close()请务必执⾏⼀次
II. 返回图⽚的⼏种⽅式封装
⼀般来说,⼀个后端提供的服务接⼝,往往是返回json数据的居多,前⾯提到了直接返回图⽚的场景,那么常见的返回图⽚有哪些⽅式呢?
1. 返回图⽚的http地址
2. 返回base64格式的图⽚
3. 直接返回⼆进制的图⽚
4. 其他...(我就见过上⾯三种,别的还真不知道)
那么我们提供的⼀个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() {
Enum(outType);
}
public MediaTypeEnum mediaType() {
Enum(mediaType);
}
}
为了简化判断,定义了两个注解,⼀个ReturnTypeEnum, ⼀个 MediaTypeEnum,当然必要性不是特别⼤,下⾯是两者的定义
public enum ReturnTypeEnum {
URL("url"),
STREAM("stream"),
BASE64("base");
private String type;
ReturnTypeEnum(String type) {
}
private static Map<String, ReturnTypeEnum> map;
static {
map = new HashMap<>(3);
for(ReturnTypeEnum e: ReturnTypeEnum.values()) {
map.pe, e);
}
}
public static ReturnTypeEnum getEnum(String type) {
if (type == null) {
return URL;
}
ReturnTypeEnum e = (LowerCase());
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) {
< = ext;
this.mime = mime;
this.magic = magic;
}
private static Map<String, MediaTypeEnum> map;
static {
map = new HashMap<>(4);
for (MediaTypeEnum e: values()) {
map.Ext(), e);
}
}
public static MediaTypeEnum getEnum(String type) {
if (type == null) {
return ImageJpg;
}
MediaTypeEnum e = (LowerCase());
return e == null ? ImageJpg : e;springmvc的注解有哪些
}
}
上⾯是请求参数封装的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 (urnType()) {
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异常
<("upload to cdn error! e:{}", e);
throw new Message());
}
}
// 返回base64
private void base64(byte[] bytes, BaseResponse response) {
String base = Encoder().encodeToString(bytes);
response.setBase(base);
}
// 返回⼆进制图⽚
private void stream(byte[] bytes, BaseRequest request) throws SelfError {
try {
MediaTypeEnum mediaType = diaType();
HttpServletResponse servletResponse = ((ServletRequestAttributes) RequestAttributes()).getResponse();
servletResponse.Mime());
OutputStream os = OutputStream();
os.write(bytes);
os.flush();
os.close();
} catch (Exception e) {
<("general return stream img error! req: {}, e:{}", request, e);
if (StringUtils.Message())) {
throw new Message());
} 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
以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论