欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2018年01月19日 09:55:02   作者:小灰灰Blog  
這篇文章主要介紹了SpringMVC返回圖片的幾種方式(小結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧

后端提供服務(wù),通常返回的json串,但是某些場(chǎng)景下可能需要直接返回二進(jìn)制流,如一個(gè)圖片編輯接口,希望直接將圖片流返回給前端,此時(shí)可以怎么處理?

I. 返回二進(jìn)制圖片

主要借助的是 HttpServletResponse這個(gè)對(duì)象,實(shí)現(xiàn)case如下

@RequestMapping(value = {"/img/render"}, method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS})
@CrossOrigin(origins = "*")
@ResponseBody
public String execute(HttpServletRequest httpServletRequest,
       HttpServletResponse httpServletResponse) {
  // img為圖片的二進(jìn)制流
  byte[] img = xxx;
  httpServletResponse.setContentType("image/png");
  OutputStream os = httpServletResponse.getOutputStream();
  os.write(img);
  os.flush();
  os.close();
  return "success";
}

注意事項(xiàng)

  1. 注意ContentType定義了圖片類型
  2. 將二進(jìn)制寫入 httpServletResponse#getOutputStream
  3. 寫完之后,flush(), close()請(qǐng)務(wù)必執(zhí)行一次

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

一般來(lái)說,一個(gè)后端提供的服務(wù)接口,往往是返回json數(shù)據(jù)的居多,前面提到了直接返回圖片的場(chǎng)景,那么常見的返回圖片有哪些方式呢?

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

那么我們提供的一個(gè)Controller,應(yīng)該如何同時(shí)支持上面這三種使用姿勢(shì)呢?

1. bean定義

因?yàn)橛袔追N不同的返回方式,至于該選擇哪一個(gè),當(dāng)然是由前端來(lái)指定了,所以,可以定義一個(gè)請(qǐng)求參數(shù)的bean對(duì)象

@Data
public class BaseRequest {
  private static final long serialVersionUID = 1146303518394712013L;
  /**
   * 輸出圖片方式:
   *
   * url : http地址 (默認(rèn)方式)
   * 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);
  }
}

為了簡(jiǎn)化判斷,定義了兩個(gè)注解,一個(gè)ReturnTypeEnum, 一個(gè) MediaTypeEnum, 當(dāng)然必要性不是特別大,下面是兩者的定義

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

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

上面是請(qǐng)求參數(shù)封裝的bean,返回當(dāng)然也有一個(gè)對(duì)應(yīng)的bean

@Data
public class BaseResponse {

  /**
   * 返回圖片的相對(duì)路徑
   */
  private String path;


  /**
   * 返回圖片的https格式
   */
  private String url;


  /**
   * base64格式的圖片
   */
  private String base;
}

說明:

實(shí)際的項(xiàng)目環(huán)境中,請(qǐng)求參數(shù)和返回肯定不會(huì)像上面這么簡(jiǎn)單,所以可以通過繼承上面的bean或者自己定義對(duì)應(yīng)的格式來(lái)實(shí)現(xiàn)

2. 返回的封裝方式

既然目標(biāo)明確,封裝可算是這個(gè)里面最清晰的一個(gè)步驟了

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 {
    // 上傳到圖片服務(wù)器,根據(jù)各自的實(shí)際情況進(jìn)行替換
    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);
}
// 返回二進(jìn)制圖片
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);
    }
  }
}

說明:

請(qǐng)無(wú)視上面的幾個(gè)自定義異常方式,需要使用時(shí),完全可以干掉這些自定義異常即可;這里簡(jiǎn)單說一下,為什么會(huì)在實(shí)際項(xiàng)目中使用這種自定義異常的方式,主要是有以下幾個(gè)優(yōu)點(diǎn)

配合全局異常捕獲(ControllerAdvie),使用起來(lái)非常方便簡(jiǎn)單

所有的異常集中處理,方便信息統(tǒng)計(jì)和報(bào)警

如,在統(tǒng)一的地方進(jìn)行異常計(jì)數(shù),然后超過某個(gè)閥值之后,報(bào)警給負(fù)責(zé)人,這樣就不需要在每個(gè)出現(xiàn)異常case的地方來(lái)主動(dòng)埋點(diǎn)了

避免錯(cuò)誤狀態(tài)碼的層層傳遞

- 這個(gè)主要針對(duì)web服務(wù),一般是在返回的json串中,會(huì)包含對(duì)應(yīng)的錯(cuò)誤狀態(tài)碼,錯(cuò)誤信息
- 而異常case是可能出現(xiàn)在任何地方的,為了保持這個(gè)異常信息,要么將這些數(shù)據(jù)層層傳遞到controller;要么就是存在ThreadLocal中;顯然這兩種方式都沒有拋異常的使用方便

有優(yōu)點(diǎn)當(dāng)然就有缺點(diǎn)了:

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

@Override
public synchronized Throwable fillInStackTrace() {
  return this;
}

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

III. 項(xiàng)目相關(guān)

只說不練好像沒什么意思,上面的這個(gè)設(shè)計(jì),完全體現(xiàn)在了我一直維護(hù)的開源項(xiàng)目 Quick-Media中,當(dāng)然實(shí)際和上面有一些不同,畢竟與業(yè)務(wù)相關(guān)較大,有興趣的可以參考

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

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

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java使用ExecutorService來(lái)停止線程服務(wù)

    Java使用ExecutorService來(lái)停止線程服務(wù)

    這篇文章主要介紹了Java使用ExecutorService來(lái)停止線程服務(wù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • java之路徑分隔符介紹

    java之路徑分隔符介紹

    考慮到程序的可移植性,創(chuàng)建文件時(shí)建議大家選用"/",因?yàn)榻?jīng)過測(cè)試用java創(chuàng)建文件時(shí)在windows平臺(tái)下用“/”也是可以的,java貌似在后臺(tái)作過處理了。
    2013-03-03
  • 詳解SpringBoot讀取resource目錄下properties文件的常見方式

    詳解SpringBoot讀取resource目錄下properties文件的常見方式

    這篇文章主要介紹了SpringBoot讀取resource目錄下properties文件的常見方式,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • JVM指令的使用深入詳解

    JVM指令的使用深入詳解

    這篇文章主要給大家介紹了關(guān)于JVM指令使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-01-01
  • 基于Java實(shí)現(xiàn)獲取本地IP地址和主機(jī)名

    基于Java實(shí)現(xiàn)獲取本地IP地址和主機(jī)名

    這篇文章主要介紹了基于Java實(shí)現(xiàn)獲取本地IP地址和主機(jī)名,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • mybatis多層嵌套resultMap及返回自定義參數(shù)詳解

    mybatis多層嵌套resultMap及返回自定義參數(shù)詳解

    這篇文章主要介紹了mybatis多層嵌套resultMap及返回自定義參數(shù)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧
    2020-12-12
  • Java將對(duì)象寫入文件讀出_序列化與反序列化的實(shí)例

    Java將對(duì)象寫入文件讀出_序列化與反序列化的實(shí)例

    下面小編就為大家?guī)?lái)一篇Java將對(duì)象寫入文件讀出_序列化與反序列化的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2017-08-08
  • Jboss Marshalling服務(wù)端無(wú)法接受消息

    Jboss Marshalling服務(wù)端無(wú)法接受消息

    這篇文章主要介紹了Jboss Marshalling服務(wù)端無(wú)法接受消息,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • 詳解如何在Java8中創(chuàng)建和使用線程池

    詳解如何在Java8中創(chuàng)建和使用線程池

    在 Java 8 中,線程池(Thread Pool)是一種管理線程資源的機(jī)制,能夠有效地控制并發(fā)執(zhí)行的線程數(shù)量,減少線程創(chuàng)建和銷毀的開銷,提高系統(tǒng)的性能,本篇文章將詳細(xì)介紹如何在 Java 8 中創(chuàng)建和使用線程池,需要的朋友可以參考下
    2024-06-06
  • 使用SpringMVC返回json字符串的實(shí)例講解

    使用SpringMVC返回json字符串的實(shí)例講解

    下面小編就為大家分享一篇使用SpringMVC返回json字符串的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧
    2018-03-03

最新評(píng)論