springboot实战代码:【如何优雅地返回图片】

本文将会总结返回图片地常用方法:

  1. 流方式返回:主要是实现方式不一样,基于response和HttpConvertMessage
  2. base64返回

说明

图片可以来自很多地方,可以是本地文件夹,也可以是网络流,或是数据库二进制,这里为了简单起见,就用本地文件。

@RestController
@RequestMapping("/")
public class ImgController {

    private String imgPath = "E:\\meme.png";

    private InputStream getImgInputStream() throws FileNotFoundException {
        return new FileInputStream(new File(imgPath));
    }

依赖

下面使用的IOUtils大家都应该很熟悉了,来自经典的common-io模块

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>

1.基于response返回

非常基础也简单的方式:

import org.apache.commons.io.IOUtils;
import org.springframework.http.MediaType;

    /**
     * 使用response输出图片流
     */
    @GetMapping("/img-response")
    public void getImage(HttpServletResponse resp) throws IOException {
        final InputStream in = getImgInputStream();
        resp.setContentType(MediaType.IMAGE_PNG_VALUE);
        IOUtils.copy(in, resp.getOutputStream());
    }

2.基于produces返回字节流

2.1返回裸字节流

我们先来看如果直接返回图片的字节流是什么情况:

    /**
     * 试试直接返回字节流,不指定content-type
     */
    @GetMapping(value = "/img-byte")
    public byte[] getImageByte() throws IOException {
        final InputStream in = getImgInputStream();
        return IOUtils.toByteArray(in);
    }

结果如下:乱码了,因为content-type不对
img-response乱码

2.2指定content-type返回字节流

使用produces指定类型:这里我们指定2种类型,看看会发生什么

    @GetMapping(value = "/img-media-type",
            produces = {MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE})
    public byte[] getImage() throws IOException {
        final InputStream in = getImgInputStream();
        return IOUtils.toByteArray(in);
    }

图片可以正常返回,但是仔细一看,response header里的content-typeimage/jpeg
img jpeg
原因在于当我们默认请求时,springboot匹配地第一种类型jpeg,虽然我们是png图片。

那我们如何让其返回地content-type为image/png呢?
通过读取源码注释我们知道:
produces
只需要在请求时传我们想要地Accept就可以了,下面是postman地示例:
image png

事实上,这个方法还可以对我们上面的/img-byte接口使用,就不会返回乱码了
img-byte
同时,如果我们传了不支持的类型,会得到406错误:
406

2.3直接下载图片文件

如果我们想直接弹出下载框,那么可以指定content-type为:application/octet-stream

    @GetMapping(value = "/img-file.png", 
    	produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE})
    public byte[] getImageFile() throws IOException {
        final InputStream in = getImgInputStream();
        return IOUtils.toByteArray(in);
    }

xiazai

    @GetMapping(value = "/img/{iconId}", produces = {MediaType.IMAGE_PNG_VALUE, MediaType.IMAGE_JPEG_VALUE})
    public byte[] getImage(@PathVariable String iconId) throws IOException {
        final GisIcon icon = iconService.getById(iconId);
        if (icon == null) {
            throw new IllegalArgumentException("不存在此图片");
        }
        final FileInputStream fio = new FileInputStream(new File(iconService.getSavePath(icon.getUsername(),
                icon.getName(), icon.getId())));
        byte[] bytes = new byte[fio.available()];
        log.info("读取文件结果:{}", fio.read(bytes, 0, fio.available()));
        return bytes;
    }

3.base64图片

import java.util.Base64;

    @GetMapping("/img-base64")
    public String getBase64Img() throws IOException {
        final byte[] bytes = IOUtils.toByteArray(getImgInputStream());
        return Base64.getEncoder().encodeToString(bytes);
    }

前端接收:

function reqListener (base64) {
  var body = document.getElementsByTagName("body");
  var img = document.createElement("img");
  img.src = "data:image/png;base64, "+base64;
  body.appendChild(img);
}

var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://localhost:8013/img-base64");
oReq.send();
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐