前段时间搞了一个二维码生成,搞定之后将代码分享出来,直接上代码
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.FileUtils;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import sun.font.FontDesignMetrics;
/**
*
* 二维码工具类
*
*
* @author naveu
* @date 2018年7月19日 下午3:08:36
*/
@SuppressWarnings("restriction")
public class QRCodeUtils2 {
private QRCodeUtils2() {}
private static final int BLACK = 0xFF000000;
private static final int WHITE = 0xFFFFFFFF;
/**
*
* 生成二维码图片对象
*
* @author naveu
* @date 2018年8月6日 下午12:44:27
* @param qrUrl 二维码链接
* @param qrLen 二维码边长,正方形
* @return
* @throws WriterException
*/
public static BufferedImage getQRImg(String qrUrl, int qrLen) throws WriterException {
Hashtable hints = new Hashtable<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix matrix = new MultiFormatWriter().encode(qrUrl, BarcodeFormat.QR_CODE, qrLen, qrLen, hints);
BufferedImage image = new BufferedImage(qrLen, qrLen, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < qrLen; x++) {
for (int y = 0; y < qrLen; y++) {
image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
}
}
return image;
}
/**
*
* 获取字体宽度
*
* @author naveu
* @date 2018年8月6日 下午12:44:36
* @param font 字体
* @param text 字符内容
* @return
*/
public static int getFontWidth(Font font, String text) {
FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);
int width = 0;
for (int i = 0; i < text.length(); i++) {
width += metrics.charWidth(text.charAt(i));
}
return width;
}
/**
*
* 生成文字图片对象
*
* @author naveu
* @date 2018年8月6日 下午12:44:39
* @param text 文字内容
* @param textFont 字体
* @param width 图片宽度,小于0将使用字体宽度
* @param height 图片高度,小于0将使用字体高度
* @param bgColor 图片背景色
* @param fontColor 字体色
* @param fontX 字体x坐标
* @param fontY 字体y坐标
* @return
* @throws Exception
*/
public static BufferedImage getTextImg(String text, Font textFont, Integer width, Integer height, Color bgColor, Color fontColor, Integer fontX, Integer fontY) throws Exception {
FontDesignMetrics metrics = FontDesignMetrics.getMetrics(textFont);
if (width < 0) {
width = getFontWidth(textFont, text);// 计算图片的宽
}
if (height < 0) {
height = metrics.getHeight();// 计算图片的高
}
// 创建一个BufferedImage对象
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = image.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
graphics.setColor(bgColor); // 先用背景色填充整张图片,也就是背景
graphics.fillRect(0, 0, width, height);// 画出矩形区域,以便于在矩形区域内写入文字
graphics.setColor(fontColor);// 再换成字体色,以便于写入文字
graphics.setFont(textFont);// 设置画笔字体
graphics.drawString(text, fontX, fontY + metrics.getAscent());// 画出一行字符串
graphics.dispose();
return image;
}
/**
*
* 将两张图片叠加成一张新的图片
* 一般情况上层图片小,下层图片大
*
* @author naveu
* @date 2018年8月6日 下午12:44:43
* @param image 下层图片
* @param img 上层图片
* @param x 上层图片叠加位置x坐标,小于0将放于中间位置
* @param y 上层图片叠加位置y坐标,小于0将放于中间位置
* @return
* @throws Exception
*/
public static BufferedImage imgOnImage(BufferedImage image, BufferedImage img, Integer x, Integer y) throws Exception {
// 内图的宽高
int innerWidth = img.getWidth();
int innerHeigh = img.getHeight();
if (y < 0) {
y = (image.getHeight() - innerHeigh) / 2;
}
if (x < 0) {
x = (image.getWidth() - innerWidth) / 2;
}
// 开始绘制图片前两个
Graphics2D g2 = image.createGraphics();
g2.drawImage(img, x, y, innerWidth, innerHeigh, null);
g2.dispose(); // 执行刷出返回合成的图片
image.flush();
return image;
}
/**
*
* 图片对象写入文件
*
* @author naveu
* @date 2018年8月6日 下午12:44:46
* @param image 图片对象
* @param imgFile 图片文件
* @throws IOException
* @throws WriterException
*/
public static void imgToFile(BufferedImage image, File imgFile) throws IOException, WriterException {
// 文件类型
final String formatName = imgFile.getName().substring(imgFile.getName().lastIndexOf(".") + 1);
ImageIO.write(image, formatName, imgFile);
}
/**
*
* 图片对象写入文件,可设置分辩率
*
* @author naveu
* @date 2018年8月8日 下午1:36:38
* @param image 图片对象
* @param imgFile 图片文件
* @param dpi 分辩率dpi值
* @throws IOException
*/
public static void imgToFile(BufferedImage image, File imgFile, Integer dpi) throws IOException {
imgFile.delete();
// 文件类型
final String formatName = imgFile.getName().substring(imgFile.getName().lastIndexOf(".") + 1);
// 粗略计算发现,设置新生成图片的dpi值在这里要除以25.4,暂不知道是为什么,不明白分辩率是如何计算的
final String dotsPerMilli = new DecimalFormat("#.00").format(dpi / 25.4);
for (Iterator iw = ImageIO.getImageWritersByFormatName(formatName); iw.hasNext();) {
ImageWriter writer = iw.next();
ImageWriteParam writeParam = writer.getDefaultWriteParam();
ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
continue;
}
/******************设置分辩率begin************************/
// 水平分辩率
IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
horiz.setAttribute("value", dotsPerMilli);
// 垂直分辩率
IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
vert.setAttribute("value", dotsPerMilli);
IIOMetadataNode dim = new IIOMetadataNode("Dimension");
dim.appendChild(horiz);
dim.appendChild(vert);
IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
root.appendChild(dim);
metadata.mergeTree("javax_imageio_1.0", root);
/******************设置分辩率end************************/
// 数据写入文件
final ImageOutputStream stream = ImageIO.createImageOutputStream(imgFile);
try {
writer.setOutput(stream);
writer.write(metadata, new IIOImage(image, null, metadata), writeParam);
} finally {
stream.close();
}
break;
}
}
/**
*
* 图片对象写入输出流
*
* @author naveu
* @date 2018年8月6日 下午12:44:50
* @param image 图片对象
* @param imgType 图片类型 PNG JPG等
* @param stream 输出流
* @throws IOException
* @throws WriterException
*/
public static void imgToStream(BufferedImage image, String imgType, OutputStream stream) throws IOException, WriterException {
ImageIO.write(image, imgType, stream);
}
/**
*
* 获取图片指定坐标处背景色
*
* @author naveu
* @date 2018年8月6日 下午12:44:54
* @param image 图片对象
* @param x x坐标
* @param y y坐标
* @return
*/
public static Color getImgRGBColor(BufferedImage image, int x, int y) {
int green = 0, red = 0, blue = 0;
Object data = image.getRaster().getDataElements(x, y, null);// 获取像素点
// ColorModel是一个用来将图片某点的rgb值分别取出的类,包括取出alpha值
red = image.getColorModel().getRed(data);
green = image.getColorModel().getGreen(data);
blue = image.getColorModel().getBlue(data);
return new Color(red, green, blue);
}
/**
*
* 文件压缩zip
*
* @author naveu
* @date 2018年8月6日 下午2:12:18
* @param files
* @param zipFile
*/
public static void zipCompress(List files, File zipFile) {
ZipArchiveOutputStream zos = null;
FileOutputStream zipFos = null;
ArchiveOutputStream archOut = null;
try {
zipFos = new FileOutputStream(zipFile);
archOut = new ArchiveStreamFactory().createArchiveOutputStream(ArchiveStreamFactory.ZIP, zipFos);
if (archOut instanceof ZipArchiveOutputStream) {
zos = (ZipArchiveOutputStream) archOut;
for (int i = 0; i < files.size(); i++) {
zos.putArchiveEntry(new ZipArchiveEntry(files.get(i), files.get(i).getName()));
zos.write(FileUtils.readFileToByteArray(files.get(i)));
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != zos) {
zos.flush();
zos.closeArchiveEntry();
zos.close();
}
if (null != archOut) {
archOut.close();
}
if (null != zipFos) {
zipFos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
*
* Image转BufferedImage
*
* @author naveu
* @date 2018年8月8日 下午1:37:41
* @param image
* @param type
* @return
*/
public static BufferedImage toBufferedImage(Image image, int type) {
int w = image.getWidth(null);
int h = image.getHeight(null);
BufferedImage result = new BufferedImage(w, h, type);
Graphics2D g = result.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return result;
}
/**
*
* 测试生成二维码方法
*
* @author naveu
* @date 2018年8月9日 下午5:01:25
* @param code
* @return
* @throws Exception
*/
private static File testQr(String code) throws Exception {
// 获取模板图片对象
BufferedImage image = ImageIO.read(new File("e:\\qr\\srcImg.png"));
// 生成二维码图片对象
BufferedImage qrImg = getQRImg("http://www.baidu.com", 130);
// 获取模板一个坐标点的背景色对象
Color bgColor = getImgRGBColor(image, 10, 10);
// 生成编号号图片对象
BufferedImage codeImg = getTextImg(code, new Font("Arial Bold", Font.BOLD, 120), 150, 150, bgColor, Color.WHITE, 0, 0);
// 将二维码图片合成到模板图片上
image = imgOnImage(image, qrImg, 245, -1);
// 将编号号图片合成到模板图片上
image = imgOnImage(image, codeImg, 30, -1);
// 创建新的图片文件
File newImg = new File("e:\\qr\\testQr\\" + code + ".png");
if (!newImg.getParentFile().exists()) {
newImg.getParentFile().mkdirs();
}
if (!newImg.exists()) {
newImg.createNewFile();
}
// 将合成的图片写入图片文件,保存本地文件
imgToFile(image, newImg, 150);
return newImg;
}
public static void main(String[] args) throws Exception {
// 编号号
String[] codes = {"01", "02", "03", "04"};
// 生成的所有图片
List qrFiles = new ArrayList<>();
for (int i = 0; i < codes.length; i++) {
qrFiles.add(testQr(codes[i]));
}
if (!qrFiles.isEmpty()) {
// 图片文件压缩包
File zipFile = new File("e:\\qr\\testCodes.zip");
if (!zipFile.getParentFile().exists()) {
zipFile.getParentFile().mkdirs();
}
if (!zipFile.exists()) {
zipFile.createNewFile();
}
// 压缩图片文件
zipCompress(qrFiles, zipFile);
// 删除生成的所有图片文件夹
File dir = qrFiles.get(0).getParentFile();
if (dir.exists() && dir.isDirectory()) {
FileUtils.deleteDirectory(dir);
}
}
}
}