后端生成图片返回给前端
环境模拟
运行环境:
环境:SpringBoot + Java,生成一张简单 PNG 图片,模拟二维码 / 验证码场景。
依赖:javax.imageio.ImageIO,JDK 自带,不需要额外引入 jar。
前置代码:
java
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ImageUtil {
/**
* 模拟生成一张图片(二维码/验证码)
*/
public static BufferedImage createTestImage() {
int width = 200;
int height = 200;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0,0,width,height);
g2d.setColor(Color.BLUE);
g2d.drawString("Demo QrCode",40,100);
g2d.dispose();
return image;
}
/**
* image转byte[]字节数组
*/
public static byte[] imageToBytes(BufferedImage image) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "png", bos);
return bos.toByteArray();
}
}
方式 1: 直接输出二进制字节流
后端:response 输出图片二进制,response.setContentType("image/png"),直接把图片字节写到 response 输出流,不返回 JSON
前端:Ajax 指定responseType:'blob'拿到 Blob,URL.createObjectURL(blob)生成临时 url 赋值 img 的 src。
特点:
优点:图片二进制直接传输,不会增大体积;适合二维码、验证码;
缺点:拿不到业务JSON状态码;Ajax 拿不到res.success;异常只能靠 HTTP 状态码判断;
适用:验证码、二维码图片接口。
示例:
后端
Java
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
@GetMapping("/demo/image/blob")
public void getImageBlob(HttpServletResponse response) throws Exception {
BufferedImage image = ImageUtil.createTestImage();
byte[] bytes = ImageUtil.imageToBytes(image);
response.setContentType("image/png");
response.setHeader("Cache-Control", "no-cache");
try(OutputStream out = response.getOutputStream()){
out.write(bytes);
out.flush();
}
}
前端
js
$.ajax({
url:"xxx",
type:"get",
xhrFields:{responseType:"blob"}, //解决后端字节流传到前端的乱码问题
success(blob){
let objUrl = URL.createObjectURL(blob); //便于图片标签解析
$("#img").attr("src",objUrl);
}
})
注意:用完调用 URL.revokeObjectURL(objUrl)释放内存。
方式 2: Base64 编码返回(返回 JSON,图片放在 data 字段)
后端:把图片转 base64 字符串,封装进 JSON 返回
Java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@GetMapping("/demo/image/base64")
@ResponseBody
public Map<String,Object> getImageBase64() throws Exception {
BufferedImage image = ImageUtil.createTestImage();
byte[] bytes = ImageUtil.imageToBytes(image);
//转为base64字符串
String base64Str = Base64.getEncoder().encodeToString(bytes);
Map<String,Object> result = new HashMap<>();
result.put("success",true);
result.put("data", base64Str);
result.put("msg","生成成功");
return result;
}
前端拿到 base64,拼接前缀 data:image/png;base64,,赋值给 img src。
json
success:function(res){
if(res.success){
$("#img").attr("src","data:image/png;base64," + res.data)
}
}
特点:
优点:可以携带业务字段(success、msg),可以统一 json 返回格式;
缺点:体积变大,膨胀大约 1.37 倍;大图不建议,适合小图片(二维码、头像)。
不适合大图片,会造成接口返回报文巨大。
方式 3: 后端把图片存文件 / 对象存储,返回图片访问 URL
后端生成图片,保存到服务器本地磁盘 / MinIO/OSS,返回图片 HTTP 访问地址。
json
{
"success":true,
"data":"http://xxx.oss-cn-beijing.aliyuncs.com/qrcode/xxx.png"
}
前端直接把 url 赋值给 img 的 src,不需要 Ajax 也可以,img 标签直接请求图片。
javascript
$("#img").attr("src",res.data);
特点:
优点:性能最好;图片由浏览器缓存;接口返回数据很小;适合大量图片;
缺点:要维护存储;需要清理过期图片;需要文件存储服务;
css
业务系统首选,大量二维码、海报都用这个。
注意:如果图片需要 token 鉴权,直接 img src 访问会不带 token,后端要做处理。
方式 4: 直接把图片接口地址写死到 img 的 src(不需要 Ajax)
后端接口直接输出图片流,不用 Ajax 请求。
css
<img src="/***/vcode?url=xxxx" />
浏览器会自动 get 请求这个接口,渲染图片。
特点:
代码最简单,不用写 Ajax;
缺点:无法自定义请求头 token;如果接口需要 token 鉴权,这种方式很难传 token;
css
适合不需要登录的验证码、公开二维码;需要鉴权的接口不能直接用 img src。
你的项目如果接口要携带 token,就不能直接写 src,只能用 ajax blob 方式。
| 方案 | 核心原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 方案 1:HTTP 二进制字节流(Blob 接收)response 输出图片,前端 ajax+Blob | 后端 Response 输出图片二进制,Content‑Type: image/png;前端 Ajax 设置responseType:blob,URL.createObjectURL()生成临时地址 |
1. 原始二进制传输,没有额外体积膨胀,传输效率高2. 支持在 Ajax 请求头携带 Token 做接口鉴权3. 不占用服务器磁盘,图片在内存生成直接输出 | 1. 响应体是图片,不能同时返回 JSON 业务字段(success/msg) ,异常只能靠 HTTP 状态码判断2. 前端需要手动处理 Blob 对象,用完要调用revokeObjectURL释放内存,否则内存泄漏3. 不能直接缓存业务数据(浏览器可以缓存 http 响应) |
二维码、验证码、小尺寸动态图片;接口需要 token 鉴权场景 |
| 方案 2:Base64(DataURL)返回 JSON | 后端图片转 base64 字符串,放到 JSON 的 data 字段返回;前端拼接data:image/png;base64,xxx赋值 src |
1. 标准 JSON 返回,可以同时返回 success、msg 等业务字段,统一项目返回体Ajax 支持 headers 传 Token 鉴权3. 图片直接嵌入页面,不会产生额外网络请求 | 1.体积膨胀约 1.37 倍,图片越大报文越大,接口响应变慢2. 无法被浏览器缓存,每次都要完整传输字符串3. 大图会造成页面 DOM 数据量大,渲染卡顿 | 小图片:二维码、头像;需要同时返回业务状态,图片尺寸不大 |
| 方案 3:存入对象存储 (OSS/MinIO) 返回 HTTP 访问 URL | 后端生成图片,保存到 MinIO/OSS/ 磁盘,返回可访问 http 图片地址,前端直接赋值 img.src | 1. 性能最优,图片独立请求,浏览器自带缓存2. 接口返回只有简短 url 字符串,接口报文极小3. 图片可以长期复用,多次前端访问不需要重复生成图片 | 1. 依赖存储服务,需要维护文件,要处理过期图片清理2. 图片资源请求无法携带 Token 请求头,鉴权需要用临时签名 URL 实现3. 会产生存储流量开销 | 大量生成的海报、业务二维码;图片需要多次复用;后台大批量导出图片场景 |
| 方案 4:img 标签 src 直接调用图片接口 | 后端输出图片字节流,<img src="图片接口地址" />,浏览器自动发起 get 请求加载图片 |
1. 前端代码最简单,不需要写 AJAX,一行 html 完成渲染2. 浏览器自动缓存图片 | 1.无法自定义请求头,不能传递 Token,接口不能做 token 鉴权2. 错误处理麻烦,不方便捕获接口异常 | 公开无需登录:验证码、无需鉴权的二维码 |