一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - SpringMVC返回圖片的幾種方式(小結)

SpringMVC返回圖片的幾種方式(小結)

2021-03-24 14:28小灰灰Blog JAVA教程

這篇文章主要介紹了SpringMVC返回圖片的幾種方式(小結),小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

后端提供服務,通常返回的json串,但是某些場景下可能需要直接返回二進制流,如一個圖片編輯接口,希望直接將圖片流返回給前端,此時可以怎么處理?

 

I. 返回二進制圖片

主要借助的是 HttpServletResponse這個對象,實現case如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@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";
}

注意事項

  1. 注意ContentType定義了圖片類型
  2. 將二進制寫入 httpServletResponse#getOutputStream
  3. 寫完之后,flush(), close()請務必執行一次

 

II. 返回圖片的幾種方式封裝

一般來說,一個后端提供的服務接口,往往是返回json數據的居多,前面提到了直接返回圖片的場景,那么常見的返回圖片有哪些方式呢?

  1. 返回圖片的http地址
  2. 返回base64格式的圖片
  3. 直接返回二進制的圖片
  4. 其他...(我就見過上面三種,別的還真不知道)

那么我們提供的一個Controller,應該如何同時支持上面這三種使用姿勢呢?

 

1. bean定義

因為有幾種不同的返回方式,至于該選擇哪一個,當然是由前端來指定了,所以,可以定義一個請求參數的bean對象

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@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, 當然必要性不是特別大,下面是兩者的定義

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public enum ReturnTypeEnum {
  URL("url"),
  STREAM("stream"),
  BASE64("base");
 
  private String type;
  ReturnTypeEnum(String type) {
    this.type = type;
  }
  private static Map<String, ReturnTypeEnum> 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;
  }
}
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@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<String, MediaTypeEnum> 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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Data
public class BaseResponse {
 
  /**
   * 返回圖片的相對路徑
   */
  private String path;
 
 
  /**
   * 返回圖片的https格式
   */
  private String url;
 
 
  /**
   * base64格式的圖片
   */
  private String base;
}

說明:

實際的項目環境中,請求參數和返回肯定不會像上面這么簡單,所以可以通過繼承上面的bean或者自己定義對應的格式來實現

 

2. 返回的封裝方式

既然目標明確,封裝可算是這個里面最清晰的一個步驟了

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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中;顯然這兩種方式都沒有拋異常的使用方便

有優點當然就有缺點了:

異常方式,額外的性能開銷,所以在自定義異常中,我都覆蓋了下面這個方法,不要完整的堆棧

?
1
2
3
4
@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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://my.oschina.net/u/566591/blog/1609293

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日韩国产成人精品视频人 | 欧美一卡二卡科技有限公司 | 精品国产91高清在线观看 | 91成人啪国产啪永久地址 | 狠狠色综合久久婷婷 | 欧美一级特黄特色大片 | 黑人性xxxⅹxxbbbbb | jj视频免费| 免费的强动漫人物的 | 放荡警察巨r麻麻出轨小说 范冰冰特黄xx大片 饭冈加奈子在线播放观看 法国老妇性xx在线播放 | b站免费网站入口 | 5月色婷婷 | 久久久伊人影院 | 1769在线视频| 美女视频91| 欧美日韩一区二区三在线 | 日本高清视频网站 | 亚洲一区二区日韩欧美gif | 暖暖在线精品日本中文 | 外女思春台湾三级 | 欧美日韩中文字幕在线视频 | 8x8x极品国产在线 | 青春草视频免费观看 | 午夜免费小视频 | chinese男同志videos | 成年人免费观看视频网站 | 成人在线免费看 | 99re在线精品视频免费 | 亚洲国产剧情中文视频在线 | 成人欧美一区二区三区白人 | 国产自拍啪啪 | 日本xxxxxxxxx59 | 青青青青青国产费线在线观看 | 久久精品亚洲热综合一本 | 青青精品视频 | 国产精品免费久久久久影院小说 | 久久亚洲精品成人 | 关晓彤一级做a爰片性色毛片 | 久久www免费人成_看片高清 | 国内精品哆啪啪 | 午夜片神马影院福利 |