写在开头
在实际开发过程中,二维码生成和导出都是常见的功能,最近遇到一个批量生成二维码信息并根据批次号批量下载二维码图片的需求,这里记录一下下载二维码图片的源码。
<dependency>
<groupId>com.google.zxinggroupId>
<artifactId>coreartifactId>
<version>3.5.1version>
dependency>
<dependency>
<groupId>com.google.zxinggroupId>
<artifactId>javaseartifactId>
<version>3.5.1version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.4version>
dependency>
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.awt.image.BufferedImage;
/**
* 单个二维码的值和图片流信息
*
* @since 2023/5/10 13:11
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class QrCodeInfo {
/**
* 二维码信息
* 也用作文件名
*/
private String qrCode;
/**
* 信息对应的二维码图片流
*/
private BufferedImage image;
}
import cn.hutool.core.util.ObjectUtil;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 二维码相关 工具类
*
*
* @since 2023/5/10 13:09
*/
public class QrCodeUtils {
/**
* 二维码宽和高
*/
private static final int WIDTH_AND_HEIGHT = 400;
/**
* 文字的X轴坐标
*/
private static final int TEXT_X = 180;
/**
* 文字的Y轴坐标
*/
private static final int TEXT_Y = 80;
/**
* 二维码参数设置
*/
private static final Map<EncodeHintType, Object> HINTS;
static {
HINTS = new EnumMap<>(EncodeHintType.class);
//设置编码
HINTS.put(EncodeHintType.CHARACTER_SET, "UTF-8");
//设置二维码的纠错级别
HINTS.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
}
/**
* 绘制二维码并返回图片流
*
* @param qrcode: 二维码值
* @date 2022/4/8 14:44
* @author YD
*/
public static QrCodeInfo createQrCode(String qrcode) {
try {
//设置对象并生成二维码
BitMatrix bitMatrix = new MultiFormatWriter().encode(qrcode, BarcodeFormat.QR_CODE, WIDTH_AND_HEIGHT, WIDTH_AND_HEIGHT, HINTS);
//创建二维码图片缓冲区
BufferedImage image = new BufferedImage(WIDTH_AND_HEIGHT, WIDTH_AND_HEIGHT, BufferedImage.TYPE_INT_RGB);
//生成二维码数组
for (int x = 0; x < bitMatrix.getWidth(); x++) {
for (int y = 0; y < bitMatrix.getHeight(); y++) {
//二维坐标整个区域:画什么颜色
image.setRGB(x, y, bitMatrix.get(x, y) ? MatrixToImageConfig.BLACK : MatrixToImageConfig.WHITE);
}
}
//创建画布的缓冲区
BufferedImage bufferedImage = new BufferedImage(WIDTH_AND_HEIGHT, WIDTH_AND_HEIGHT, BufferedImage.TYPE_4BYTE_ABGR);
//创建画布
Graphics2D g2d = bufferedImage.createGraphics();
//设置背景颜色
g2d.setBackground(Color.WHITE);
//开启文字抗锯齿
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
//绘制二维码
g2d.drawImage(image, 10, 30, image.getWidth(), image.getHeight(), null);
//设置画笔颜色
g2d.setColor(Color.BLACK);
Font fontChinese = new Font("微软雅黑", Font.PLAIN, 16);
///设置二维码文本字体
g2d.setFont(fontChinese);
// 绘制文字(文字绘制在二维图图片的上方中间位置)
g2d.drawString(qrcode, TEXT_X, TEXT_Y);
//销毁画笔
g2d.dispose();
bufferedImage.flush();
image = bufferedImage;
return QrCodeInfo.builder().qrCode(qrcode).image(image).build();
} catch (WriterException e) {
e.printStackTrace();
// 抛出异常了
return null;
}
}
/**
* 将BufferedImage转换为InputStream
*
* @param image: 图片流
* @date 2022/4/8 15:29
* @author YD
*/
public static InputStream bufferedImageToInputStream(BufferedImage image) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image, "png", os);
try (InputStream input = new ByteArrayInputStream(os.toByteArray())) {
return input;
}
}
/**
* 把生成的二维码打进压缩包并下载
*
* @param qrCodeInfos: 二维码信息和流的集合
* @param batchId 批次号
* @date 2022/4/8 15:26
* @author YD
*/
public static void compressQrCodeToZip(List<QrCodeInfo> qrCodeInfos, Long batchId, HttpServletResponse httpServletResponse) {
try {
//重置HttpServletResponse防止乱码
httpServletResponse.reset();
ZipOutputStream zos = new ZipOutputStream(httpServletResponse.getOutputStream());
//文件的名称
String downloadFilename = batchId + "二维码压缩包.zip";
//转换中文否则可能会产生乱码
downloadFilename = URLEncoder.encode(downloadFilename, "UTF-8");
// 指明response的返回对象是文件流
httpServletResponse.setContentType("application/octet-stream; charset=UTF-8");
//设置下载格式和名称
httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + downloadFilename);
//添加二维码进zip压缩包
qrCodeInfos.forEach(
qrCodeInfo -> {
try {
byte[] buf = new byte[8192];
int len;
//添加二维码进zip
ZipEntry entry = new ZipEntry(qrCodeInfo.getQrCode() + ".png");
zos.putNextEntry(entry);
InputStream inputStream = bufferedImageToInputStream(qrCodeInfo.getImage());
//输出压缩包
while ((len = inputStream.read(buf)) > 0) {
zos.write(buf, 0, len);
}
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
);
zos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 下载单个二维码
*
* @param qrCodeInfo: 二维码值和流信息
* @param httpServletResponse: http响应
* @date 2022/4/8 15:37
* @author YD
*/
public static void singleQrCode(QrCodeInfo qrCodeInfo, HttpServletResponse httpServletResponse) {
if (ObjectUtil.isNotNull(qrCodeInfo)) {
try {
//重置HttpServletResponse防止乱码
httpServletResponse.reset();
//文件的名称
String downloadFilename = qrCodeInfo.getQrCode() + ".png";
//转换中文否则可能会产生乱码
downloadFilename = URLEncoder.encode(downloadFilename, "UTF-8");
// 指明response的返回对象是文件流
httpServletResponse.setContentType("application/octet-stream; charset=UTF-8");
//设置下载格式和名称
httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + downloadFilename);
//输出二维码到前端
ImageIO.write(qrCodeInfo.getImage(), "png", httpServletResponse.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
import cn.hutool.core.util.ObjectUtil;
import com.wangzd.djframework.boot.common.core.util.qrcode.QrCodeInfo;
import com.wangzd.djframework.boot.common.core.util.qrcode.QrCodeUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* 二维码 测试
*
* @author wangzhongdong
* @since 2023/5/10 13:30
*/
@RestController
@RequestMapping("/qrCode")
public class QrCodeController {
static String[] ARR = {"TA000001", "TA00000A",
"TA00000B", "TA00002S",
"TA00002T", "TA000032",
"TA000033", "TA0000RS"};
/**
* 批量下载二维码
*
* @param httpServletResponse
*/
@GetMapping("/downLoadZip")
public void downLoadZip(HttpServletResponse httpServletResponse) {
List<QrCodeInfo> qrCodeInfos = new ArrayList<>();
for (String qrCode : ARR) {
QrCodeInfo qrCodeInfo = QrCodeUtils.createQrCode(qrCode);
if (ObjectUtil.isNotNull(qrCodeInfo)) {
//这里把需要生成的二维码然后添加进bufferedImages集合里
qrCodeInfos.add(qrCodeInfo);
}
}
// 测试批量导出
QrCodeUtils.compressQrCodeToZip(qrCodeInfos, 1111L httpServletResponse);
}
/**
* 生成并下载单个二维码图片
*
* @param httpServletResponse
*/
@GetMapping("downloadOne")
public void downloadOne(HttpServletResponse httpServletResponse) {
QrCodeInfo qrCodeInfo = QrCodeUtils.createQrCode(ARR[2]);
// 测试单个导出
QrCodeUtils.singleQrCode(qrCodeInfo, httpServletResponse);
}
}