SpringMVC returns images in several ways (Summary). springmvc returns Images
A json string is usually returned when the backend provides services. However, in some scenarios, a binary stream may be returned directly. For example, an image editing interface is used to directly return the image stream to the front-end. How can this problem be solved?
I. Return the Binary Image
We mainly use the HttpServletResponse object to implement the following case:
@ RequestMapping (value = {"/img/render"}, method = {RequestMethod. GET, RequestMethod. POST, RequestMethod. OPTIONS}) @ CrossOrigin (origins = "*") @ ResponseBodypublic String execute (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {// img is the binary stream byte [] img = xxx; httpServletResponse. setContentType ("image/png"); OutputStream OS = httpServletResponse. getOutputStream (); OS. write (img); OS. flush (); OS. close (); return "success ";}
Notes
- Note that ContentType defines the image type.
- Write binary data to httpServletResponse # getOutputStream
- After writing, flush () and close () must be executed once.
II. encapsulation of returned Images
Generally, a service interface provided by the backend usually returns a majority of json data. The scenario of directly returning images is mentioned above. What are the common methods of returning images?
- Http address of the returned Image
- Returns an image in base64 format.
- Returns a binary image directly.
- Others... (I have seen the above three types, but I really don't know about them)
So how can we support the above three positions at the same time for a Controller we provide?
1. bean Definition
Because there are several different return methods, you can define the bean object of a request parameter as to which one to choose is specified by the frontend.
@ Datapublic class BaseRequest {private static final long serialVersionUID = 1146303518394712013L;/*** output image method: ** url: http address (default) * base64: base64 encoded * stream: directly return the image **/private String outType;/*** type of the returned image * jpg | png | webp | gif */private String mediaType; public ReturnTypeEnum returnType () {return ReturnTypeEnum. getEnum (outType);} public MediaTypeEnum mediaType () {return MediaTypeEnum. getEnum (mediaType );}}
Two annotations, one ReturnTypeEnum and one MediaTypeEnum, are defined to simplify the judgment. Of course, the necessity is not very large. The following is the definition of the two.
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; }}
The above is the bean encapsulated by request parameters. Of course, a corresponding bean is returned.
@ Datapublic class BaseResponse {/*** returns the relative path of the image */private String path;/*** returns the https format of the image */private String url; /*** base64 Format Image */private String base ;}
Note:
In the actual project environment, the request parameters and responses are certainly not as simple as the above, so you can inherit the bean above or customize the corresponding format to achieve
2. Returned Encapsulation Method
Since the goal is clear, encapsulation is the clearest step in it.
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 {// upload to the image server, replace String path = UploadUtil according to actual conditions. uplo Ad (bytes); if (StringUtils. isBlank (path) {// upload failure throw new InternalError (null);} response. setPath (path); response. setUrl (CdnUtil. img (path);} catch (IOException e) {// cdn exception log. error ("upload to cdn error! E: {} ", e); throw new CDNUploadError (e. getMessage () ;}}// return base64private void base64 (byte [] bytes, BaseResponse response) {String base = Base64.getEncoder (). encodeToString (bytes); response. setBase (base);} // returns the binary image private void stream (byte [] bytes, BaseRequest request) throws SelfError {try {MediaTypeEnum mediaType = request. mediaType (); HttpServletResponse servletResponse = (ServletRequestAtt Ributes) 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 );}}}
Note:
Ignore the preceding custom exception methods. When you need to use them, you can completely eliminate these custom exceptions, why does this custom exception occur in actual projects? It has the following advantages:
Combined with global exception capture (ControllerAdvie), it is easy to use
Centralized handling of all exceptions to facilitate information statistics and alarms
For example, if the exception count is performed in a unified place and the threshold value is exceeded, an alarm is sent to the owner. In this way, you do not need to actively track the exception in each case.
Prevents layer-by-layer transmission of error Status Codes
-This is mainly for web Services. Generally, the returned json string contains the corresponding error status code and error information.
-Exception cases may occur anywhere. To maintain this exception information, either the data is transferred to the controller layer by layer or ThreadLocal is present; obviously, no exception is thrown in both methods.
Of course, there are disadvantages:
Exception mode and extra performance overhead. Therefore, in custom exceptions, I overwrite the following method and do not use the complete stack.
@Overridepublic synchronized Throwable fillInStackTrace() { return this;}
Coding habits. Some people may dislike this method very much.
III. project-related
It doesn't seem interesting to say that you don't practice it. The above design is completely reflected in the open-source project Quick-Media that I have been maintaining. Of course, it is actually different from the above, after all, it is highly relevant to the business. If you are interested, refer
QuickMedia: https://github.com/liuyueyi/quick-media:
BaseAction: com. hust. hui. quickmedia. web. wxapi. WxBaseAction # buildReturn
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.