import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.wxy.base.enumerate.HttpResult;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @Author
* @Date: 2022/10/19 9:45
*/
public class QrcodeUtil {
/**
* 默认编码方式
*/
public static final String DEFAULT_CHARSET = "UTF-8";
/**
* 默认二维码图片格式
*/
public static final String DEFAULT_SUBFIX = "PNG";
/**
* 生成二维码默认宽度
*/
public static final int DEFAULT_WIDTH = 250;
/**
* 生成二维码默认高度
*/
public static final int DEFAULT_HEIGHT = 300;
/**
* 默认二维码中间log宽度
*/
public static final int DEFAULT_LOG_WIDTH = 50;
/**
* 默认二维码中间log高度
*/
public static final int DEFAULT_LOG_HEIGHT = 50;
/**
* log默认路径
*/
public static final String DEFAULT_LOG_PATH = Objects.requireNonNull(QrcodeUtil.class.getClassLoader().getResource("log/company_logo.jpg")).getPath();
/**
* 由字符串生成二维码BufferedImage对象
*
* @param content 字符串内容
* @param width 二维码宽度,如果为空或小于等于0采用默认宽度
* @param height 二维码高度,如果为空或小于等于0采用默认高度
* @return
*/
private static BufferedImage createQrCodeBufferedImage(String content, Integer width, Integer height) throws WriterException {
BufferedImage resultImage = null;
if (!StringUtils.isEmpty(content)) {
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, DEFAULT_CHARSET);// 指定字符编码为UTF-8
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);// 指定二维码的纠错等级为中级
hints.put(EncodeHintType.MARGIN, 2);// 设置图片的边距
QRCodeWriter writer = new QRCodeWriter();
width = width != null && width > 0 ? width : DEFAULT_WIDTH;
height = height != null && height > 0 ? height : DEFAULT_HEIGHT;
BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
// 写到字节数据中
// MatrixToImageWriter.writeToStream(bitMatrix, DEFAULT_SUBFIX, os);
// resultImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
// ImageIO.write(resultImage, DEFAULT_SUBFIX, os);
// 写到文件中
// MatrixToImageWriter.writeToPath(bitMatrix, DEFAULT_SUBFIX,
// Paths.get(DEFAULT_PATH));
resultImage = new BufferedImage(bitMatrix.getWidth(), bitMatrix.getHeight(), BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
// resultImage.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF008000 : 0xFFFFFFFF);// 0xFF000000 黑色 0xFF008000 绿色
resultImage.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);// 0xFF000000 黑色 0xFFFFFFFF 白色
}
}
}
return resultImage;
}
/**
* 由字符串生成base64格式的复杂二维码
*
* @param content 字符串内容
* @param width 二维码宽度,如果为空或小于等于0采用默认宽度
* @param height 二维码高度,如果为空或小于等于0采用默认高度
* @param isSaveToPath 是否保存到文件中
* @param logPath log图片的路径
* @param isFixedLogSize 是否固定log图片大小
* @param text 二维码底部文本内容
* @return
*/
public static BufferedImage productQrCodeWithLog(String content, Integer width, Integer height, boolean isSaveToPath,
String logPath, boolean isFixedLogSize, String text) {
if (StringUtils.isNotBlank(content)) {
try {
width = width != null && width > 0 ? width : DEFAULT_WIDTH;
height = height != null && height > 0 ? height : DEFAULT_HEIGHT;
BufferedImage image = createQrCodeBufferedImage(content, width, height);
if (image != null) {
// ByteArrayOutputStream os = new ByteArrayOutputStream();
insertImageAndText(image, width, height, logPath, isFixedLogSize, text);
// ImageIO.write(image, DEFAULT_SUBFIX, os);
return image;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* 插入LOGO
*
* @param source 二维码图片
* @param width 二维码宽度,如果为空或小于等于0采用默认宽度
* @param height 二维码高度,如果为空或小于等于0采用默认高度
* @param imgPath LOGO图片地址
* @param isFixedLogSize 是否固定二维码中间log图标大小
* @param text 二维码底部文本内容
* @throws Exception
*/
private static void insertImageAndText(BufferedImage source, Integer width, Integer height, String imgPath,
boolean isFixedLogSize, String text) throws Exception {
File file = new File(imgPath);
if (!file.exists()) {
System.err.println("" + imgPath + " 该文件不存在!");
return;
}
Image src = ImageIO.read(new File(imgPath));
int lwidth = src.getWidth(null);
int lheight = src.getHeight(null);
if (isFixedLogSize || lwidth >= width || lheight >= height) { // 固定LOGO大小
if (lwidth > width) {
lwidth = DEFAULT_LOG_WIDTH;
}
if (lheight > height) {
lheight = DEFAULT_LOG_HEIGHT;
}
Image image = src.getScaledInstance(lwidth, lheight, Image.SCALE_SMOOTH);
BufferedImage tag = new BufferedImage(lwidth, lheight, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(image, 0, 0, null); // 绘制缩小后的图
g.dispose();
src = image;
}
// 插入LOGO
Graphics2D graph = source.createGraphics();
int x = (width - lwidth) / 2;
int y = (height - lheight) / 2;
graph.drawImage(src, x, y, lwidth, lheight, null);
Shape shape = new RoundRectangle2D.Float(x, y, lwidth, lheight, 6, 6);
graph.setStroke(new BasicStroke(3f));
graph.draw(shape);
if (!StringUtils.isEmpty(text)) {
int fontStyle = 1;
int fontSize = 12; //
// 计算文字开始的位置(居中显示)
// x开始的位置:(图片宽度-字体大小*字的个数)/2
int startX = (width - (fontSize * text.length())) / 2;
// y开始的位置:图片高度-(图片高度-图片宽度)/2
int startY = height - (height - width) / 2;
graph.setColor(Color.BLUE);
graph.setFont(new Font(null, fontStyle, fontSize)); // 字体风格与字体大小 graph.drawString(text, startX, startY);
graph.drawString(text, startX, startY);
}
graph.dispose();
}
/**
* 解码二维码内容
*
* @param file
* @return
* @throws Exception
*/
public static String decode(File file) throws Exception {
BufferedImage image;
image = ImageIO.read(file);
if (image == null) {
return null;
}
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result;
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
hints.put(DecodeHintType.CHARACTER_SET, DEFAULT_CHARSET);
result = new MultiFormatReader().decode(bitmap, hints);
return result.getText();
}
/**
* 生成多个二维码图片,压缩成zip写入响应流
*
* @param response 响应
* @param contentList 二维码内容列表
* @throws IOException
*/
public static void downloadQrCodeZip(HttpServletResponse response, List<QrCodeContent> contentList) throws IOException {
downloadQrCodeZip(response, contentList, "二维码.zip", DEFAULT_LOG_PATH, 400);
}
/**
* 生成多个二维码图片,压缩成zip写入响应流
*
* @param response 响应
* @param contentList 二维码内容列表
* @param downloadFileName 下载的zip文件名
* @param logPath log路径
* @param qrCodeImageWidthAndHeight 二维码宽高(是正方形的二维码,宽高一致)
* @throws IOException
*/
public static void downloadQrCodeZip(HttpServletResponse response,
List<QrCodeContent> contentList,
String downloadFileName,
String logPath,
int qrCodeImageWidthAndHeight
) throws IOException {
response.setContentType("multipart/form-data");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(downloadFileName, "UTF-8")); // 压缩包名
try (ServletOutputStream outputStream = response.getOutputStream();
ZipOutputStream zos = new ZipOutputStream(outputStream)) {
if (CollectionUtils.isEmpty(contentList)) {
return;
}
for (int i = 0; i < contentList.size(); i++) {
QrCodeContent qrCodeContent = contentList.get(i);
BufferedImage bufferedImage = productQrCodeWithLog(
qrCodeContent.getContent(),
qrCodeImageWidthAndHeight,
qrCodeImageWidthAndHeight,
false,
logPath,
true,
qrCodeContent.getTitle()
);
try (ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
ImageOutputStream imageOutputStream = new MemoryCacheImageOutputStream(byteOutputStream)) {
ImageIO.write(bufferedImage, qrCodeContent.getSuffix(), imageOutputStream);
zos.putNextEntry(new ZipEntry(qrCodeContent.getFileName()));
byte[] bytes = byteOutputStream.toByteArray();
zos.write(bytes);
}
}
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class QrCodeContent {
/**
* 二维码内容(一般是url)
*/
private String content;
/**
* 生成二维码图片的文件名(比如xxx.png或者xxx.jpeg)
*/
private String fileName;
/**
* 二维码底部文本内容(可为空)
*/
private String title;
/**
* 获取文件后缀
*
* @return
*/
public String getSuffix() {
int lastIndex = fileName.lastIndexOf(".");
return fileName.substring(lastIndex + 1);
}
}
/**
* 生成多个二维码图片,压缩成zip写入响应流
*
* @param contentList 二维码内容列表
* @throws IOException
*/
public static void wriQrCodeZip(List<QrCodeContent> contentList) throws IOException {
wriQrCodeZip(contentList, "二维码.zip", DEFAULT_LOG_PATH, 400);
}
public static void wriQrCodeZip(
List<QrCodeContent> contentList,
String downloadFileName,
String logPath,
int qrCodeImageWidthAndHeight
) throws IOException {
try (
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
ImageOutputStream imageOutputStream = new MemoryCacheImageOutputStream(byteOutputStream);
FileOutputStream fileOutputStream = new FileOutputStream("\\Users\\18521\\Desktop\\r_code.zip");
ZipOutputStream zos = new ZipOutputStream(fileOutputStream)
) {
if (CollectionUtils.isEmpty(contentList)) {
return;
}
for (int i = 0; i < contentList.size(); i++) {
QrCodeContent qrCodeContent = contentList.get(i);
BufferedImage bufferedImage = productQrCodeWithLog(
qrCodeContent.getContent(),
qrCodeImageWidthAndHeight,
qrCodeImageWidthAndHeight,
false,
logPath,
true,
qrCodeContent.getTitle()
);
ImageIO.write(bufferedImage, qrCodeContent.getSuffix(), imageOutputStream);
zos.putNextEntry(new ZipEntry(qrCodeContent.getFileName()));
byte[] bytes = byteOutputStream.toByteArray();
zos.write(bytes);
}
}
}
public static void main(String[] args) throws IOException {
}
}
java二维码批量生成并打包为zip下载
于 2022-10-19 17:00:03 首次发布