亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

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

2024-07-13 10:16:57
字體:
來源:轉載
供稿:網友

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

I. 返回二進制圖片

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

@RequestMapping(value = {"/img/render"}, method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS})@CrossOrigin(origins = "*")@ResponseBodypublic 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對象

@Datapublic 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<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;  }}
@Datapublic 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

@Datapublic 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());  }}// 返回base64private 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中;顯然這兩種方式都沒有拋異常的使用方便

有優點當然就有缺點了:

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

@Overridepublic synchronized Throwable fillInStackTrace() {  return this;}

編碼習慣問題,有些人可能就非常不喜歡這種使用方式

III. 項目相關

只說不練好像沒什么意思,上面的這個設計,完全體現在了我一直維護的開源項目 Quick-Media中,當然實際和上面有一些不同,畢竟與業務相關較大,有興趣的可以參考

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

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国外成人在线直播| 一夜七次郎国产精品亚洲| 日韩av免费在线看| 欧美成人精品一区二区| 日韩亚洲成人av在线| xvideos成人免费中文版| 欧美日韩激情美女| 久久韩剧网电视剧| 91欧美激情另类亚洲| 色七七影院综合| 久久人人爽人人| 色www亚洲国产张柏芝| 狠狠躁18三区二区一区| 国产精品久久久久久久久| 日本精品久久中文字幕佐佐木| 亚洲精品有码在线| 国产精品香蕉在线观看| 欧美诱惑福利视频| 亚洲国产精品美女| 欧美午夜精品伦理| 夜夜狂射影院欧美极品| 亚洲天堂男人天堂女人天堂| 国产视频在线观看一区二区| 欧美高清视频一区二区| 欧美大学生性色视频| 欧美国产日韩二区| 热99精品里视频精品| 69av成年福利视频| 国产欧美日韩专区发布| 久久影视电视剧凤归四时歌| 亚洲三级 欧美三级| 欧美高清视频一区二区| 欧美日韩免费观看中文| 韩国福利视频一区| 精品国产一区二区三区久久久| 亚洲第一男人天堂| 色综合久综合久久综合久鬼88| 2019日本中文字幕| 成人信息集中地欧美| 国产精品成人一区二区| 国产精品久久电影观看| 成人有码在线播放| 国产精品羞羞答答| 永久免费毛片在线播放不卡| 亚洲xxxxx| 久久精品国产亚洲精品2020| 成人免费高清完整版在线观看| 亚洲成人精品久久久| 国产精品专区h在线观看| 国产一区二区三区直播精品电影| 欧美激情在线有限公司| 欧美高清视频在线| 免费不卡欧美自拍视频| 国产精品免费网站| 久久久久久久国产精品视频| 欧美日韩国产综合视频在线观看中文| 欧美电影免费在线观看| 亚洲国产精品热久久| 久久国产一区二区三区| 国产精品av在线播放| 成人激情视频在线播放| 色妞欧美日韩在线| 国产精品美女网站| 91视频88av| 色妞欧美日韩在线| 亚洲国产精品推荐| 国产色婷婷国产综合在线理论片a| 日本欧美爱爱爱| 国产成人综合av| xvideos国产精品| 中日韩午夜理伦电影免费| 久久亚洲精品国产亚洲老地址| 国语自产在线不卡| 亚洲视频欧美视频| 久久精品99久久香蕉国产色戒| 精品国产拍在线观看| 国产亚洲欧美视频| 国产成人精品免高潮费视频| 97视频在线观看视频免费视频| 国产欧美韩国高清| 最新日韩中文字幕| 亚洲无av在线中文字幕| 久久人人97超碰精品888| 91精品国产综合久久香蕉的用户体验| www.久久色.com| 国产一区二区三区高清在线观看| 国产精品九九久久久久久久| 91高清免费在线观看| 中文字幕不卡av| 欧美激情久久久久久| 日韩精品免费在线观看| 亚洲国产精久久久久久| 欧美最猛黑人xxxx黑人猛叫黄| 国语自产精品视频在线看一大j8| 国产欧美日韩丝袜精品一区| 97在线视频免费| 欧美极品少妇xxxxⅹ裸体艺术| 日韩电影大全免费观看2023年上| 91免费的视频在线播放| 国产精品美女午夜av| 国产一区二区三区网站| 久久黄色av网站| 97视频在线观看成人| 久久久久久久久久久91| 日韩免费观看视频| 国产99久久精品一区二区 夜夜躁日日躁| 久久在线观看视频| 国产精品一区专区欧美日韩| 久久人人看视频| 欧美另类暴力丝袜| 最近2019好看的中文字幕免费| 91免费福利视频| 久久久久久久久亚洲| 久久影视电视剧免费网站清宫辞电视| 欧美国产欧美亚洲国产日韩mv天天看完整| 欧美成人免费全部观看天天性色| 亚洲人成在线观| 国产精品一区二区性色av| 91久久精品视频| 精品国内产的精品视频在线观看| 国产精品高潮视频| 国产欧美日韩综合精品| 97色在线视频观看| 国产中文字幕亚洲| 日韩精品免费电影| 日韩av日韩在线观看| yellow中文字幕久久| 黑人巨大精品欧美一区二区三区| 亚洲国产天堂久久国产91| 国产精品视频白浆免费视频| 久热精品视频在线观看一区| 2019日本中文字幕| 中文字幕自拍vr一区二区三区| 精品久久久久久亚洲国产300| 久久成人18免费网站| 亚洲a∨日韩av高清在线观看| 国产精品一区二区三区在线播放| 国产精品中文字幕久久久| 欧美精品久久久久久久久久| 日韩在线播放一区| 国产日韩欧美电影在线观看| 亚洲图中文字幕| 久久777国产线看观看精品| 久久国产精品偷| 最近中文字幕2019免费| 26uuu国产精品视频| 日韩精品在线视频观看| 亚洲二区中文字幕| 性日韩欧美在线视频| 91爱爱小视频k| 国产狼人综合免费视频| 欧美福利视频网站| 欧美激情免费在线| 91系列在线观看| 国产一区二区三区视频免费| 久久这里只有精品99| 欧美性高潮床叫视频| 这里只有精品视频| 日本一区二区在线免费播放| 欧美成人一区在线| 色综合久久中文字幕综合网小说| 久久精品国产亚洲精品| 美女少妇精品视频| 亚洲最大福利视频网站|