Web 應用中動態生成圖片(驗證碼、統計圖表、實時監控圖、個性化海報)是常見需求。本文詳述基於 HttpServlet 配合 java.awt 繪圖 API,在內存中構建圖像並以 JPEG/PNG 格式直接輸出至 HTTP 響應流的完整實現路徑。
瀏覽器請求靜態圖片時,服務器返回標準 HTTP 響應,Content-Type 為 image/jpeg 或 image/png,體為圖片二進制數據。Servlet 動態生成圖片同理:
response.setContentType("image/jpeg")response.getOutputStream()int width = 400, height = 300;
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
TYPE_INT_RGB:24 位 RGB,無透明通道,適合照片、圖表TYPE_INT_ARGB:32 位含 Alpha,適合需透明背景的圖層疊加Graphics2D g = bi.createGraphics();
Graphics2D 提供反鋸齒、描邊、填充、仿射變換等高級繪圖能力。
// 背景
g.setBackground(Color.WHITE);
g.clearRect(0, 0, width, height);
// 反鋸齒
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 繪製圖形
g.setColor(Color.BLUE);
g.fillRect(50, 50, 200, 100);
g.setColor(Color.RED);
g.setFont(new Font("微軟正黑體", Font.BOLD, 24));
g.drawString("動態生成圖片", 80, 120);
// 釋放資源
g.dispose();
bi.flush();
方案一:ImageIO 標準 API(推薦,JDK 9+ 兼容)
OutputStream out = response.getOutputStream();
ImageIO.write(bi, "jpeg", out); // 或 "png"
out.flush();
方案二:JPEGCodec 精細控制品質(舊代碼常見,JDK 內部 API,JDK 9+ 已移除)
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bi);
param.setQuality(0.9f, false); // 0.0~1.0,false=非無損
encoder.setJPEGEncodeParam(param);
encoder.encode(bi);
package com.example.web.util;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
public class DynamicImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 1. 設置響應頭
resp.setContentType("image/jpeg");
resp.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // 防緩存
resp.setDateHeader("Expires", 0);
// 2. 業務參數解析(如圖表類型、數據源等)
String type = req.getParameter("type"); // 可擴展
// 3. 繪圖
int w = 600, h = 400;
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
try {
renderChart(g, w, h, type); // 核心繪圖邏輯解耦
} finally {
g.dispose();
}
// 4. 編碼輸出
try (OutputStream out = resp.getOutputStream()) {
ImageIO.write(image, "jpeg", out);
}
}
private void renderChart(Graphics2D g, int w, int h, String type) {
// 背景
g.setColor(new Color(0xF5F5F5));
g.fillRect(0, 0, w, h);
// 示例:繪製簡單折線圖
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLACK);
g.drawRect(50, 50, w - 100, h - 100); // 座標系邊框
int[] xData = {100, 180, 260, 340, 420, 500};
int[] yData = {300, 220, 280, 180, 240, 150};
g.setColor(Color.BLUE);
for (int i = 0; i < xData.length - 1; i++) {
g.drawLine(xData[i], yData[i], xData[i+1], yData[i+1]);
}
// 可擴展:坐標軸、刻度、圖例、數據標籤等
}
}
BufferedImage 池化(需線程安全),減少 GC 壓力。CompletableFuture 或獨立渲染服務,Servlet 僅返回任務 ID 或重定向。ETag/Last-Modified 支援條件請求。wqy-zenhei、Microsoft YaHei)Font.createFont(Font.TRUETYPE_FONT, inputStream) 動態註冊apt-get install -y fontconfig fonts-wqy-zenhei| 格式 | 適用場景 | 透明度 | 壓縮 |
|---|---|---|---|
| JPEG | 照片、漸變圖、不需透明 | 無 | 有損,品質可調 |
| PNG | 圖表、文字、需透明、線條銳利 | 支援 | 無損 |
| WebP | 現代瀏覽器,體積更小 | 支援 | 有損/無損 |