需求:要求后端生成如下图的二维码(图中信息为随意添加),前端调接口直接下载图片或压缩包
思路:因要求生成的二维码样式较多(未展示出来),所以我这把这张图片分成了三个部分,标题,二维码和详情,拼接到一张图片上,代码如下
import com.alibaba.fastjson.JSONObject;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import lombok.extern.slf4j.Slf4j;
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import javax.swing.filechooser.FileSystemView;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@Slf4j
public class QrCodeUtils {
private BufferedImage image;
/**
* 图片的宽度
*/
private static final int IMAGE_WIDTH = 380;
/**
* 图片的高度
*/
private static final int IMAGE_HEIGHT = 210;
/**
* 二维码颜色 默认是黑色
*/
private static final int QR_COLOR = 0xFF000000;
/**
* 二维码宽
*/
private static final int WIDTH = 100;
/**
* 二维码高
*/
private static final int HEIGHT = 100;
/**
* 单次生成的二维码数量
*/
private static final int BATCH_NUM = 50;
/**
* 文字颜色
*/
private static final Color WORD_COLOR = Color.black;
/**
* 背景颜色
*/
private static final Color BG_COLOR = Color.white;
/**
* 线条颜色
*/
private static final Color LINE_COLOR = new Color(205, 205, 180);
/**
* 背景图形颜色
*/
private static final Color BG_IMAGE_COLOR = new Color(246, 246, 246);
/**
* 标题文字样式
*/
private static final Font TITLE_FONT = new Font("黑体", Font.BOLD, 16);
/**
* 内容文字样式
*/
private static final Font FONT = new Font("宋体", Font.PLAIN, 14);
/**
* 用于设置QR二维码参数
*/
private static final Map<EncodeHintType, Object> HINTS = new HashMap<EncodeHintType, Object>() {
private static final long serialVersionUID = 1L;
{
// 设置QR二维码的纠错级别(H为最高级别)具体级别信息
put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
// 设置编码方式
put(EncodeHintType.CHARACTER_SET, "utf-8");
put(EncodeHintType.MARGIN, 0);
}
};
/**
* 生成图片文件到本地
*
* @param fileLocation fileLocation
*/
public void outputImage(String fileLocation) {
BufferedOutputStream bos = null;
if (image != null) {
try {
FileOutputStream fos = new FileOutputStream(fileLocation + ".jpg");
bos = new BufferedOutputStream(fos);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bos);
encoder.encode(image);
bos.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭输出流
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 下载图片
* @param response response
*/
private void downloadImg(HttpServletResponse response) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
//写入流中
ImageIO.write(image, "png", baos);
} catch (IOException e) {
e.printStackTrace();
}
byte[] bytes = baos.toByteArray();
response.reset();
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
response.addHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Content-Disposition", "attachment;filename=" + System.currentTimeMillis() + ".png");
toClient.write(bytes);
toClient.flush();
toClient.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
/**
* 输出base64图片
* @return String
*/
private String outputBase64() {
//io流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
//写入流中
ImageIO.write(image, "png", baos);
} catch (IOException e) {
e.printStackTrace();
}
//转换成字节
byte[] bytes = baos.toByteArray();
BASE64Encoder encoder64 = new BASE64Encoder();
//转换成base64串
String pngBase64 = encoder64.encodeBuffer(bytes).trim();
//删除
pngBase64 = pngBase64.replaceAll("\n", "").replaceAll("\r", "");
return "data:image/jpg;base64," + pngBase64;
}
/**
* 临时路径
* @return String
*/
public static String getTemplatePath() {
String realPath = QrCodeUtils.class.getClassLoader().getResource("").getFile();
File file = new File(realPath);
realPath = file.getAbsolutePath();
try {
realPath = java.net.URLDecoder.decode(realPath, "utf-8");
} catch (Exception e) {
e.printStackTrace();
}
return realPath;
}
/**
* 输出到压缩包
* @param zipFile zipFile
* @param list list
*/
public void zipFiles(File zipFile, List<JSONObject> list) {
// 判断压缩后的文件存在不,不存在则创建
if (!zipFile.exists()) {
try {
zipFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
// 创建 FileOutputStream 对象
FileOutputStream fileOutputStream ;
// 创建 ZipOutputStream
ZipOutputStream zipOutputStream ;
// 创建 FileInputStream 对象
BufferedInputStream bis = null;
InputStream inputStream = null;
try {
// 实例化 FileOutputStream 对象
fileOutputStream = new FileOutputStream(zipFile);
// 实例化 ZipOutputStream 对象
zipOutputStream = new ZipOutputStream(fileOutputStream);
// 遍历源文件数组
for (JSONObject jsonObject : list) {
long timestamp = System.currentTimeMillis();
// 二维码
BufferedImage qrImage = drawQRCode(jsonObject.getString("url"));
// 总数量
int totalNum = jsonObject.getInteger("num");
// 已完成数量
int num = 0;
double times = 1;
// 如果数量大于50,分页生成
if (jsonObject.getInteger("num") > BATCH_NUM) {
times = (int)Math.ceil((double)totalNum/BATCH_NUM);
}
for (int i = 0; i < times; i++) {
int tampNum = BATCH_NUM;
if (num + tampNum > totalNum) {
tampNum = totalNum - num;
}
drawImg(jsonObject.getString("title"), jsonObject.getString("words") + timestamp,
qrImage, tampNum, num, jsonObject.getString("type"));
num = num + tampNum;
//io流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
//写入流中
ImageIO.write(image, "jpg", baos);
} catch (IOException e) {
e.printStackTrace();
}
//转换成字节
byte[] bytes = baos.toByteArray();
// 实例化 ZipEntry 对象,源文件数组中的当前文件
zipOutputStream.putNextEntry(new ZipEntry(System.currentTimeMillis() + ".jpg"));
zipOutputStream.write(bytes, 0, bytes.length);
}
}
zipOutputStream.closeEntry();
zipOutputStream.close();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bis != null) {
bis.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void drawImg(String title, String words, BufferedImage qrImage, Integer num, Integer startNum, String type) {
//创建主模板图片
image = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT * num, BufferedImage.TYPE_INT_RGB);
for (int a = 0; a < num; a++) {
Graphics2D main = image.createGraphics();
//设置图片的背景色
main.setColor(new Color(255, 250, 240));
main.fillRect(0, a * IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_HEIGHT * (a + 1));
switch (type) {
default:
drawImg01(title, words, qrImage, a, startNum);
break;
}
}
}
public void drawImg01(String title, String words, BufferedImage qrImage, int a, int startNum) {
// 标题区域宽度、高度
int titleAreaWidth = IMAGE_WIDTH;
int titleAreaHeight = 60;
// 二维码区域宽度、高度
int codeAreaWidth = 140;
int codeAreaHeight = IMAGE_HEIGHT - titleAreaHeight - 10;
// 文字区域宽度、高度
int fontAreaWidth = IMAGE_WIDTH - codeAreaWidth;
int fontAreaHeight = IMAGE_HEIGHT - titleAreaHeight - 10;
// ***********************页面头部文字****************
Graphics2D titleRight = image.createGraphics();
titleRight.setColor(BG_COLOR);
titleRight.fillRect(0, a * IMAGE_HEIGHT, titleAreaWidth, titleAreaHeight);
titleRight.drawImage(drawTitle(title), 125, a * IMAGE_HEIGHT + (titleAreaHeight - 35), null);
titleRight.setColor(LINE_COLOR);
titleRight.drawLine(20, a * IMAGE_HEIGHT + (titleAreaHeight - 5), 380, a * IMAGE_HEIGHT + (titleAreaHeight - 5));
// 左上
titleRight.drawLine(5, a * IMAGE_HEIGHT + 5, 15, a * IMAGE_HEIGHT + 5);
titleRight.drawLine(5, a * IMAGE_HEIGHT + 5, 5, a * IMAGE_HEIGHT + 15);
// 右上
titleRight.drawLine(IMAGE_WIDTH - 15, a * IMAGE_HEIGHT + 5, IMAGE_WIDTH - 5, a * IMAGE_HEIGHT + 5);
titleRight.drawLine(IMAGE_WIDTH - 5, a * IMAGE_HEIGHT + 5, IMAGE_WIDTH - 5, a * IMAGE_HEIGHT + 15);
// ***********************二维码****************
Graphics2D leftBg = image.createGraphics();
leftBg.setColor(BG_COLOR);
leftBg.fillRect(0, a * IMAGE_HEIGHT + titleAreaHeight, codeAreaWidth, IMAGE_HEIGHT - 70);
leftBg.drawImage(qrImage, 35, a * IMAGE_HEIGHT + 70, qrImage.getWidth(), qrImage.getHeight(), null);
// 左下
leftBg.setColor(LINE_COLOR);
leftBg.drawLine(5, (a + 1) * IMAGE_HEIGHT - 25, 5, (a + 1) * IMAGE_HEIGHT - 15);
leftBg.drawLine(5, (a + 1) * IMAGE_HEIGHT - 15, 15, (a + 1) * IMAGE_HEIGHT - 15);
//**********************中间文字部分*********
Graphics2D centerWord = image.createGraphics();
centerWord.setColor(BG_COLOR);
centerWord.fillRect(codeAreaWidth, a * IMAGE_HEIGHT + titleAreaHeight, fontAreaWidth, fontAreaHeight);
centerWord.drawImage(drawInfo(words, startNum + a, 30), codeAreaWidth + 20, a * IMAGE_HEIGHT + titleAreaHeight + 15, null);
// 右下
centerWord.setColor(LINE_COLOR);
centerWord.drawLine(IMAGE_WIDTH - 15, (a + 1) * IMAGE_HEIGHT - 15, IMAGE_WIDTH - 5, (a + 1) * IMAGE_HEIGHT - 15);
centerWord.drawLine(IMAGE_WIDTH - 5, (a + 1) * IMAGE_HEIGHT - 15, IMAGE_WIDTH - 5, (a + 1) * IMAGE_HEIGHT - 25);
}
/**
* @param qrUrl 二维码内容
* @return 二维码图片
*/
public static BufferedImage drawQRCode(String qrUrl) {
try {
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
// 参数顺序分别为:编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
BitMatrix bm = multiFormatWriter.encode(qrUrl, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, HINTS);
BufferedImage qrImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
// 边框
Graphics2D border = qrImage.createGraphics();
border.setColor(LINE_COLOR);
// 上
border.drawLine(0, 0, WIDTH, 0);
// 左
border.drawLine(0, 0, 0, HEIGHT);
// 下
border.drawLine(0, HEIGHT - 2, WIDTH - 2, HEIGHT - 2);
// 右
border.drawLine(WIDTH - 2, 0, WIDTH - 2, HEIGHT - 2);
// 开始利用二维码数据创建Bitmap图片,分别设为黑(0xFFFFFFFF)白(0xFF000000)两色
for (int x = 1; x < WIDTH - 4; x++) {
for (int y = 1; y < HEIGHT - 4; y++) {
qrImage.setRGB(x, y, bm.get(x, y) ? QR_COLOR : 0);
}
}
return qrImage;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 标题
* @param title 标题内容
* @return BufferedImage
*/
public static BufferedImage drawTitle(String title) {
BufferedImage titleImage = new BufferedImage(200, 20, BufferedImage.TYPE_INT_ARGB);
Graphics2D titleInfo = titleImage.createGraphics();
// 设置字体颜色
titleInfo.setColor(WORD_COLOR);
titleInfo.setFont(TITLE_FONT);
titleInfo.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
titleInfo.drawString(title, 0, 15);
return titleImage;
}
/**
* 内容
* @param words 内容
* @param num 第num个
* @param margin 文字行间距
* @return BufferedImage
*/
public static BufferedImage drawInfo(String words, int num, int margin) {
BufferedImage infoImage = new BufferedImage(200, 80, BufferedImage.TYPE_INT_ARGB);
Graphics2D centerWord = infoImage.createGraphics();
//设置字体颜色
centerWord.setColor(WORD_COLOR);
centerWord.setFont(FONT);
centerWord.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
String[] info = words.split("&");
centerWord.drawString(info[0], 0, 15);
centerWord.drawString(info[1], 0, 15 + margin);
String tamp = "0000";
int len = String.valueOf(num).length();
tamp = tamp.substring(0, tamp.length() - len) + num;
centerWord.drawString(info[2] + tamp, 0, 15 + 2 * margin);
return infoImage;
}
/**
* 下载二维码图片
* @param list list
* @param response response
*/
public static void getCodeImg(List<JSONObject> list, HttpServletResponse response) {
QrCodeUtils cg = new QrCodeUtils();
for (JSONObject jsonObject : list) {
// 二维码
BufferedImage image = drawQRCode(jsonObject.getString("url"));
cg.drawImg(jsonObject.getString("title"), jsonObject.getString("words"),
image, 2, 0, jsonObject.getString("type"));
cg.downloadImg(response);
}
}
/**
* 下载zip文件
* @param list 二维码上的内容,包含标题,产地,产品信息,二维码信息等
* @param response response
*/
public static void getCodeZip(List<JSONObject> list, HttpServletResponse response) {
QrCodeUtils cg = new QrCodeUtils();
String name ="qrCodeImg.zip";
File filePath = new File(getTemplatePath() + File.separator + name);
cg.zipFiles(filePath, list);
String filename = System.currentTimeMillis() + "_" + name;
//设置文件路径
if (filePath.exists()) {
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition", "attachment; filename=" + new String(filename.getBytes("utf-8"), "ISO8859-1"));
byte[] buffer = new byte[4096];
fis = new FileInputStream(filePath);
bis = new BufferedInputStream(fis);
OutputStream os = response.getOutputStream();
int i = bis.read(buffer);
while (i != -1) {
os.write(buffer, 0, i);
i = bis.read(buffer);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
// 删除临时文件
filePath.delete();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
QrCodeUtils cg = new QrCodeUtils();
// 保存图片的地址
FileSystemView fsv = FileSystemView.getFileSystemView();
File path = fsv.getHomeDirectory();
String outUrl = path.getAbsolutePath() + "\imgs\";
String info = "产品名称:信阳毛尖&产地:河南信阳&溯源编码:";
// 二维码内容
String url = "https://baike.baidu.com/item/%E4%BF%A1%E9%98%B3%E6%AF%9B%E5%B0%96/548774?fr=aladdin";
String type = "1";
// 二维码
BufferedImage image = drawQRCode(url);
try {
// 画图(标题,详情,二维码,一张图片上的二维码数量,起始数量,二维码样式)
cg.drawImg("信阳特产,信阳毛尖", info, image, 1, 0, type);
// 输出图片
cg.outputImage(outUrl + System.currentTimeMillis());
} catch (Exception e) {
e.printStackTrace();
}
}
}