条形码/二维码生成探索
所用依赖
<!--条形码生成依赖(轻量型,推荐使用这个)(生成条码的同时会把信息生成到条形码下)-->
<dependency>
<groupId>net.sf.barcode4j</groupId>
<artifactId>barcode4j-light</artifactId>
<version>${barcode4j-light.version}</version>
</dependency>
<!--条形码(生成的条形码无法生成条形码下由编码的那种条形码)和二维码生成依赖-->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>${google.zxing.version}</version>
</dependency>
生成条形码工具类(含条形码下面有字符和没有字符的)
package com.yb.bar.qr.code.utils;
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.oned.Code128Writer;
import org.krysalis.barcode4j.impl.code128.Code128Bean;
import org.krysalis.barcode4j.output.bitmap.BitmapCanvasProvider;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/**
* 条形码下由字符的条形码生成(推荐使用)
*
* @author yunzhi-yangbiao
* @date 2020/1/9
*/
public class BarCodeUtils {
private static final String FORMAT = "image/png";
/**
* 通过输出流获取条形码(条形码编码的内容就是显示在条形码下的字符)
* <p></>
* 如果需要解码直接返回字符即可,因为这个是显示出来的
*
* @param content 条形码的编码内容
* @param barCodePath 条形码的路径
* @throws Exception
*/
public static void barCodeFile(String content, String barCodePath) throws Exception {
OutputStream outputStream = new FileOutputStream(new File(barCodePath));
getBarCode(content, outputStream);
}
/**
* 通过输出流获取条形码(条形码编码的内容就是显示在条形码下的字符)
* <p></>
* 如果需要解码直接返回字符即可,因为这个是显示出来的
*
* @param content 条形码的编码内容
* @param outputStream 输出流
* @throws Exception
*/
public static void getBarCode(String content, OutputStream outputStream) throws Exception {
//选择条形码类型(好多类型可供选择)
Code128Bean bean = new Code128Bean();
//设置长宽
final int resolution = 150;
final double moduleWidth = 0.20;
bean.setModuleWidth(moduleWidth);
bean.doQuietZone(false);
//配置画布
BitmapCanvasProvider canvas = new BitmapCanvasProvider(outputStream, FORMAT, resolution,
BufferedImage.TYPE_BYTE_BINARY, false, 0);
//生成条码
bean.generateBarcode(canvas, content);
canvas.finish();
}
/**
* 解析条形码
*
* @param imgPath 条形码路径
* @return
*/
public static String barCodeDecode(String imgPath) throws Exception {
BufferedImage image = ImageIO.read(new File(imgPath));
if (image == null) {
throw new RuntimeException("the decode image may be not exists.");
}
LuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result = new MultiFormatReader().decode(bitmap, null);
return result.getText();
}
//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
/**
* 生成条形码(条形码下边没有字符,但是可以解析)
* <p></>
* 注意:这个只能解析此类里条形码生成后下面没有字符的条形码(用的是zxing依赖)
*
* @param content 条形码编码内容(使用CODE_128编码)
* @return
* @throws WriterException
*/
public static BufferedImage barCodeEncode(String content) throws WriterException {
return barCodeEncode(content, BarcodeFormat.CODE_128);
}
/**
* ZXing 条码边距及总宽度-默认计算规则
* codeWidth:自定义的条码宽度
* fullWidth:条码根据编码内容自动生成编码数组长度(new Code128Writer().encode(contents).length)+边距MARGIN
* outputWidth:codeWidth 与 fullWidth 的最大值
* 放大倍数(取整) int multiple = outputWidth / fullWidth;
* 边距 int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
* 生成条码长度为: outputWidth + 2 * leftPadding
* <p></>
* 生成条形码(条形码下边没有字符,但是可以解析)
*
* @param content 条形码编码内容
* @param barcodeFormat 条形码编码方式(推荐使用CODE_128)
* @return
* @throws WriterException
*/
public static BufferedImage barCodeEncode(String content, BarcodeFormat barcodeFormat) throws WriterException {
//定义二维码内容参数
Map<EncodeHintType, Object> hints = new HashMap<>(2);
//设置条码两边空白边距为0,默认为10,如果宽度不是条码自动生成宽度的倍数则MARGIN无效
hints.put(EncodeHintType.MARGIN, 0);
//设置字符集编码格式
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//为了无边距,需设置宽度为条码自动生成规则的宽度
int width = new Code128Writer().encode(content).length;
//前端可控制高度,不影响识别
int height = 70;
//条码放大倍数(实测1倍,可以生成条形码,但是无法解析)
int codeMultiples = 2;
//获取条码内容的宽,不含两边距,当EncodeHintType.MARGIN为0时即为条码宽度
int codeWidth = width * codeMultiples;
// 生成条形码,参数顺序分别为:编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
BitMatrix matrix = new MultiFormatWriter().encode(content, barcodeFormat, codeWidth, height, hints);
return MatrixToImageWriter.toBufferedImage(matrix);
}
}
二维码生成工具类(简单点的,也可以设置颜色)
package com.yb.bar.qr.code.utils;
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.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.oned.Code128Writer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
/**
* 生成彩色带图片的二维码
*
* @author yunzhi-yangbiao
* @date 2020/1/7
*/
public class ColourQrCodeUtils {
private static final int FRAME_WIDTH = 2;
private static final int IMAGE_WIDTH = 500;
private static final int IMAGE_HEIGHT = IMAGE_WIDTH;
private static final int IMAGE_WIDTH_A = 100;
private static final int IMAGE_HEIGHT_A = IMAGE_WIDTH_A;
public static final String DEFAULT_IMAGE_SUFFIX = "jpg";
private static final int IMAGE_HALF_WIDTH = IMAGE_WIDTH_A / 2;
private static MultiFormatWriter multiWriter = new MultiFormatWriter();
/**
* 生成彩色的二维码到指定路径,如果有srcImagePath不为空,则是带有图片的彩色二维码,否则就只是彩色二维码
*
* @param content 二维码显示的文本
* @param widthHeight 二维码的宽高(宽高一致为正方形)
* @param destImagePath 二维码存放路径
*/
public static void qrEncodeImageFile(String content, int widthHeight, String destImagePath) throws Exception {
qrEncodeImageFile(content, widthHeight, null, destImagePath);
}
/**
* 生成彩色的二维码到指定路径,如果有srcImagePath不为空,则是带有图片的彩色二维码,否则就只是彩色二维码
*
* @param content 二维码显示的文本
* @param destImagePath 二维码存放路径
*/
public static void qrEncodeImageFile(String content, String destImagePath) throws Exception {
qrEncodeImageFile(content, IMAGE_WIDTH, null, destImagePath);
}
/**
* 生成彩色的二维码到指定路径,如果有srcImagePath不为空,则是带有图片的彩色二维码,否则就只是彩色二维码
*
* @param content 二维码显示的文本
* @param widthHeight 二维码的宽高(宽高一致为正方形)
* @param destImagePath 二维码存放路径
* @param srcImagePath 加入二维码的图片路径(路径直取用resources后面部分即可)
*/
public static void qrEncodeImageFile(String content, int widthHeight, String srcImagePath, String destImagePath) throws Exception {
//获取图片缓冲区
BufferedImage bufferedImage = genBarcode(content, widthHeight, widthHeight, srcImagePath, BarcodeFormat.QR_CODE);
//生成二维码图片文件(srcImagePath为不为空时就时生成的就是把图片加入二维码的二维码)
ImageIO.write(bufferedImage, DEFAULT_IMAGE_SUFFIX, new File(destImagePath));
}
/**
* 生成彩色的二维码,如果有srcImagePath不为空,则是带有图片的彩色二维码,否则就只是彩色二维码
* 生成彩色带图片的二维码的图片缓冲区,可用响应流输出到页面
*
* @param content 二维码显示的文本
* @param widthHeight 二维码的宽高(宽高一致为正方形)
* @return
* @throws Exception
*/
public static BufferedImage qrEncodeBufferedImage(String content, int widthHeight) throws Exception {
return qrEncodeBufferedImage(content, widthHeight, null);
}
/**
* 生成彩色的二维码,如果有srcImagePath不为空,则是带有图片的彩色二维码,否则就只是彩色二维码
* 生成彩色带图片的二维码的图片缓冲区,可用响应流输出到页面
*
* @param content 二维码显示的文本
* @return
* @throws Exception
*/
public static BufferedImage qrEncodeBufferedImage(String content) throws Exception {
return qrEncodeBufferedImage(content, IMAGE_WIDTH, null);
}
/**
* 生成彩色的二维码,如果有srcImagePath不为空,则是带有图片的彩色二维码,否则就只是彩色二维码
* 生成彩色带图片的二维码的图片缓冲区,可用响应流输出到页面
*
* @param content 二维码显示的文本
* @return
* @throws Exception
*/
public static BufferedImage qrEncodeBufferedImage(String content, String srcImagePath) throws Exception {
return qrEncodeBufferedImage(content, IMAGE_WIDTH, srcImagePath);
}
/**
* 生成彩色的二维码,如果有srcImagePath不为空,则是带有图片的彩色二维码,否则就只是彩色二维码
* 生成彩色带图片的二维码的图片缓冲区,可用响应流输出到页面
*
* @param content 二维码显示的文本
* @param widthHeight 二维码的宽高(宽高一致为正方形)
* @param srcImagePath 加入二维码的图片路径(路径直取用resources后面部分即可)
* @return
* @throws Exception
*/
public static BufferedImage qrEncodeBufferedImage(String content, int widthHeight, String srcImagePath) throws Exception {
//获取二维码图片缓冲区
return genBarcode(content, widthHeight, widthHeight, srcImagePath, BarcodeFormat.QR_CODE);
}
/**
* 生成彩色的二维码,如果有srcImagePath不为空,则是带有图片的彩色二维码,否则就只是彩色二维码
* 生成彩色带图片的二维码的输入流,可以直接通过输入流上传到fastdfs文件系统
*
* @param content 二维码显示的文本
* @param widthHeight 二维码的宽高(宽高一致为正方形)
* @return
* @throws Exception
*/
public static InputStream qrEncodeInputStream(String content, int widthHeight) throws Exception {
//获取生成二维码的输入流
return qrEncodeInputStream(content, widthHeight, null);
}
/**
* 生成彩色的二维码,如果有srcImagePath不为空,则是带有图片的彩色二维码,否则就只是彩色二维码
* 生成彩色带图片的二维码的输入流,可以直接通过输入流上传到fastdfs文件系统
*
* @param content 二维码显示的文本
* @return
* @throws Exception
*/
public static InputStream qrEncodeInputStream(String content) throws Exception {
//获取生成二维码的输入流
return qrEncodeInputStream(content, IMAGE_WIDTH, null);
}
/**
* 生成彩色的二维码,如果有srcImagePath不为空,则是带有图片的彩色二维码,否则就只是彩色二维码
* 生成彩色带图片的二维码的输入流,可以直接通过输入流上传到fastdfs文件系统
*
* @param content 二维码显示的文本
* @param srcImagePath 二维码显示的文本(路径直取用resources后面部分即可)
* @return
* @throws Exception
*/
public static InputStream qrEncodeInputStream(String content, String srcImagePath) throws Exception {
//获取生成二维码的输入流
return qrEncodeInputStream(content, IMAGE_WIDTH, srcImagePath);
}
/**
* 生成彩色的二维码,如果有srcImagePath不为空,则是带有图片的彩色二维码,否则就只是彩色二维码
* 生成彩色带图片的二维码的输入流,可以直接通过输入流上传到fastdfs文件系统
*
* @param content 二维码显示的文本
* @param widthHeight 二维码的宽高(宽高一致为正方形)
* @param srcImagePath 加入二维码的图片路径(路径直取用resources后面部分即可)
* @return
* @throws Exception
*/
public static InputStream qrEncodeInputStream(String content, int widthHeight, String srcImagePath) throws Exception {
//获取二维码图片缓冲区
BufferedImage bufferedImage = genBarcode(content, widthHeight, widthHeight, srcImagePath, BarcodeFormat.QR_CODE);
//获取生成二维码的输入流
return getInputStream(bufferedImage);
}
/**
* 获取不带插入图片的二维码缓冲图像
*
* @param content 二维码显示的文本
* @param barcodeFormat 条码格式(CODE_128是条形码/QR_CODE是二维码)
* @return
* @throws Exception
*/
public static BufferedImage getBufferedImage(String content, BarcodeFormat barcodeFormat) throws Exception {
return getBufferedImage(content, IMAGE_WIDTH, IMAGE_HEIGHT, barcodeFormat);
}
/**
* 获取不带插入图片的二维码缓冲图像(像微信插入圆弧矩形图片)
*
* @param content 二维码显示的文本
* @return
* @throws Exception
*/
public static BufferedImage getBufferedImage(String content, String logoPath) throws Exception {
BufferedImage bufferedImage = getBufferedImage(content, BarcodeFormat.QR_CODE);
return SimpleQrCodeUtils.logoMatrix(bufferedImage, logoPath);
}
/**
* 获取不带插入图片的二维码缓冲图像
*
* @param content 二维码显示的文本
* @return
* @throws Exception
*/
public static BufferedImage getBufferedImage(String content) throws Exception {
return getBufferedImage(content, BarcodeFormat.QR_CODE);
}
/**
* 获取不带插入图片的二维码缓冲图像(像微信插入圆弧矩形图片)
*
* @param content 二维码显示的文本
* @param widthHeight 二维码的宽高(宽高一致为正方形)
* @return
* @throws Exception
*/
public static BufferedImage getBufferedImage(String content, int widthHeight, String logoPath) throws Exception {
BufferedImage bufferedImage = getBufferedImage(content, widthHeight, widthHeight, BarcodeFormat.QR_CODE);
return SimpleQrCodeUtils.logoMatrix(bufferedImage, logoPath);
}
/**
* 获取不带插入图片的二维码缓冲图像
*
* @param content 二维码显示的文本
* @param widthHeight 二维码的宽高(宽高一致为正方形)
* @return
* @throws Exception
*/
public static BufferedImage getBufferedImage(String content, int widthHeight) throws Exception {
return getBufferedImage(content, widthHeight, widthHeight, BarcodeFormat.QR_CODE);
}
/**
* 获取不带插入图片的二维码缓冲图像(设置私有是因为条形码的时候不需要去加logo图)
*
* @param content 二维码显示的文本
* @param width 二维码的宽度
* @param height 二维码的高度
* @param barcodeFormat 条码格式(CODE_128是条形码/QR_CODE是二维码)
* @return
* @throws Exception
*/
private static BufferedImage getBufferedImage(String content, int width, int height, BarcodeFormat barcodeFormat, String logoPath) throws Exception {
BufferedImage bufferedImage = getBufferedImage(content, width, height, barcodeFormat);
return SimpleQrCodeUtils.logoMatrix(bufferedImage, logoPath);
}
/**
* 获取不带插入图片的二维码缓冲图像
*
* @param content 二维码显示的文本
* @param width 二维码的宽度
* @param height 二维码的高度
* @param barcodeFormat 条码格式(CODE_128是条形码/QR_CODE是二维码)
* @return
* @throws Exception
*/
public static BufferedImage getBufferedImage(String content, int width, int height, BarcodeFormat barcodeFormat) throws Exception {
//定义二维码内容参数
Map<EncodeHintType, Object> hints = new HashMap<>(2);
//设置字符集编码格式
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//设置容错等级,在这里我们使用M级别
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
// 生成二维码,参数顺序分别为:编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
BitMatrix matrix = new MultiFormatWriter().encode(content, barcodeFormat, width, height, hints);
//判断是否是QR_CODE二维码,不是则当成条形码来看待
if (!BarcodeFormat.QR_CODE.equals(barcodeFormat)) {
//这样就能更好的兼容条形码了,二维码就使用QR_CODE就OK了
return MatrixToImageWriter.toBufferedImage(matrix);
}
// 二维矩阵转为一维像素数组
int[] pixels = new int[width * height];
//设置二维码颜色
for (int y = 0; y < matrix.getHeight(); y++) {
for (int x = 0; x < matrix.getWidth(); x++) {
//设置颜色
setColor(matrix, y, x, width, pixels);
}
}
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
image.getRaster().setDataElements(0, 0, width, height, pixels);
return image;
}
/**
* 得到BufferedImage
*
* @param content 二维码显示的文本
* @param width 二维码的宽度
* @param height 二维码的高度
* @param barcodeFormat 条码格式(CODE_128是条形码/QR_CODE是二维码)
* @param srcImagePath 插入二维码的图片路径(路径直取用resources后面部分即可)
* @return
* @throws WriterException
* @throws IOException
*/
private static BufferedImage genBarcode(String content, int width, int height, String srcImagePath, BarcodeFormat barcodeFormat) throws Exception {
//判断是否是需要加入图片到二维码,如果srcImagePath为空,则表示是生成不带图片的二维码
if (!StringUtils.hasText(srcImagePath)) {
//获取不带插入图片的二维码缓冲图像
return getBufferedImage(content, width, height, barcodeFormat);
}
// 读取源图像
BufferedImage scaleImage = scale(srcImagePath, IMAGE_WIDTH_A, IMAGE_HEIGHT_A, true);
int[][] srcPixels = new int[IMAGE_WIDTH_A][IMAGE_HEIGHT_A];
for (int i = 0; i < scaleImage.getWidth(); i++) {
for (int j = 0; j < scaleImage.getHeight(); j++) {
//图片的像素点
srcPixels[i][j] = scaleImage.getRGB(i, j);
}
}
//编码
Map hints = new Hashtable<EncodeHintType, String>();
//这里选择H也就是大的错误纠正级别,也就是二维码的信息变多,M和H就是明显的对比
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
// 生成二维码
BitMatrix matrix = multiWriter.encode(content, barcodeFormat, width, height, hints);
// 二维矩阵转为一维像素数组
int halfW = matrix.getWidth() / 2;
int halfH = matrix.getHeight() / 2;
int[] pixels = new int[width * height];
//设置二维码颜色
for (int y = 0; y < matrix.getHeight(); y++) {
for (int x = 0; x < matrix.getWidth(); x++) {
//获取判断条件否则报黄线,出现复杂判断
boolean flagA = x > halfW - IMAGE_HALF_WIDTH
&& x < halfW + IMAGE_HALF_WIDTH
&& y > halfH - IMAGE_HALF_WIDTH
&& y < halfH + IMAGE_HALF_WIDTH;
boolean flagB = x > halfW - IMAGE_HALF_WIDTH - FRAME_WIDTH
&& x < halfW - IMAGE_HALF_WIDTH + FRAME_WIDTH
&& y > halfH - IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH
+ IMAGE_HALF_WIDTH + FRAME_WIDTH;
boolean flagC = x > halfW + IMAGE_HALF_WIDTH - FRAME_WIDTH
&& x < halfW + IMAGE_HALF_WIDTH + FRAME_WIDTH
&& y > halfH - IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH
+ IMAGE_HALF_WIDTH + FRAME_WIDTH;
boolean flagD = x > halfW - IMAGE_HALF_WIDTH - FRAME_WIDTH
&& x < halfW + IMAGE_HALF_WIDTH + FRAME_WIDTH
&& y > halfH - IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH
- IMAGE_HALF_WIDTH + FRAME_WIDTH;
boolean flagE = x > halfW - IMAGE_HALF_WIDTH - FRAME_WIDTH
&& x < halfW + IMAGE_HALF_WIDTH + FRAME_WIDTH
&& y > halfH + IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH
+ IMAGE_HALF_WIDTH + FRAME_WIDTH;
// 左上角颜色,根据自己需要调整颜色范围和颜色(500长宽对应H是155,只有长宽是500才在角上配色)
if (x > 0 && x < 140 && y > 0 && y < 140 && width == 500 && height == 500) {
Color color = new Color(68, 34, 231);
int colorInt = color.getRGB();
pixels[y * width + x] = matrix.get(x, y) ? colorInt : 16777215;
// 读取图片
} else if (x > 360 && x < 500 && y > 0 && y < 140 && width == 500 && height == 500) {
Color color = new Color(231, 211, 94);
int colorInt = color.getRGB();
pixels[y * width + x] = matrix.get(x, y) ? colorInt : 16777215;
// 读取图片
} else if (x > 0 && x < 140 && y > 360 && y < 500 && width == 500 && height == 500) {
Color color = new Color(148, 13, 231);
int colorInt = color.getRGB();
pixels[y * width + x] = matrix.get(x, y) ? colorInt : 16777215;
// 读取图片
} else if (flagA) {
pixels[y * width + x] = srcPixels[x - halfW + IMAGE_HALF_WIDTH][y - halfH + IMAGE_HALF_WIDTH];
// 在图片四周形成边框
} else if (flagB || flagC || flagD || flagE) {
pixels[y * width + x] = 0xfffffff;
} else {
//设置颜色
setColor(matrix, y, x, width, pixels);
}
}
}
//获取图片缓冲区对象
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
image.getRaster().setDataElements(0, 0, width, height, pixels);
return image;
}
/**
* 设置颜色方法--抽取
*
* @param matrix
* @param y
* @param x
* @param width
* @param pixels
*/
public static void setColor(BitMatrix matrix, int y, int x, int width, int[] pixels) {
// 二维码颜色(RGB)
int num1 = (int) (50 - (50.0 - 13.0) / matrix.getHeight() * (y + 1));
int num2 = (int) (165 - (165.0 - 72.0) / matrix.getHeight() * (y + 1));
int num3 = (int) (162 - (162.0 - 107.0) / matrix.getHeight() * (y + 1));
Color color = new Color(num1, num2, num3);
//查看到colorInt的值为-13523807(大概是草绿色),0xFF000000(黑色)
int colorInt = color.getRGB();
// 此处可以修改二维码的颜色,可以分别制定二维码和背景的颜色//0x000000:0xffffff//16777215(白色)
pixels[y * width + x] = matrix.get(x, y) ? colorInt : 16777215;
}
/**
* 把传入的原始图像按高度和宽度进行缩放,生成符合要求的图标
*
* @param srcImageFile 源文件地址(路径直取用resources后面部分即可)
* @param height 目标高度
* @param width 目标宽度
* @param hasFiller 比例不对时是否需要补白:true为补白; false为不补白;
* @throws IOException
*/
private static BufferedImage scale(String srcImageFile, int width, int height, boolean hasFiller) throws IOException {
// 缩放比例
double ratio;
//获取指定目录文件(这个是打成jar后获取的方式,如果是在resources下,直接使用对应路径即可,例如/static/a.png)
ClassPathResource resource = new ClassPathResource(srcImageFile);
//读取图片文件到图片缓冲区(只有获取输入流的方式不会有问题,getFile在jar的情况有问题)
BufferedImage srcImage = ImageIO.read(resource.getInputStream());
Image destImage = srcImage.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH);
// 计算比例
if ((srcImage.getHeight() > height) || (srcImage.getWidth() > width)) {
if (srcImage.getHeight() > srcImage.getWidth()) {
ratio = (new Integer(height)).doubleValue()
/ srcImage.getHeight();
} else {
ratio = (new Integer(width)).doubleValue()
/ srcImage.getWidth();
}
AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
destImage = op.filter(srcImage, null);
}
// 补白
if (hasFiller) {
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D graphic = image.createGraphics();
graphic.setColor(Color.white);
graphic.fillRect(0, 0, width, height);
if (width == destImage.getWidth(null)) {
//画图片
graphic.drawImage(destImage, 0, (height - destImage.getHeight(null)) / 2,
destImage.getWidth(null), destImage.getHeight(null), Color.white, null);
} else {
graphic.drawImage(destImage, (width - destImage.getWidth(null)) / 2, 0,
destImage.getWidth(null), destImage.getHeight(null), Color.white, null);
graphic.dispose();
destImage = image;
}
}
return (BufferedImage) destImage;
}
/**
* 获取BufferedImage的InputStream流
* 可以通过此输入流直接上传到fastdfs服务器
*
* @param bufferedImage 图片缓冲区对象
* @return
*/
public static InputStream getInputStream(BufferedImage bufferedImage) throws Exception {
ByteArrayOutputStream bs = new ByteArrayOutputStream();
ImageOutputStream imOut = ImageIO.createImageOutputStream(bs);
ImageIO.write(bufferedImage, DEFAULT_IMAGE_SUFFIX, imOut);
InputStream is = new ByteArrayInputStream(bs.toByteArray());
return is;
}
}
二维码生成工具类(稍微复杂点的,可以设置颜色还有其他的部分颜色)
package com.yb.bar.qr.code.utils;
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.decoder.ErrorCorrectionLevel;
import org.springframework.core.io.ClassPathResource;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
import java.util.Objects;
/**
* 二维码生成工具
*
* @author yunzhi-yangbiao
* @date 2020/1/7
*/
public class SimpleQrCodeUtils {
/**
* 编码格式,采用utf-8
*/
private static final String UNICODE = "utf-8";
/**
* 图片格式
*/
private static final String FORMAT = "JPG";
/**
* 二维码宽度,单位:像素pixels
*/
private static int QR_CODE_WIDTH = 300;
/**
* 二维码高度,单位:像素pixels
*/
private static final int QR_CODE_HEIGHT = QR_CODE_WIDTH;
/**
* LOGO宽度,单位:像素pixels
*/
private static final int LOGO_WIDTH = 70;
/**
* LOGO高度,单位:像素pixels
*/
private static final int LOGO_HEIGHT = LOGO_WIDTH;
/**
* 生成二维码图片
* =====如果logoPath为空,则不进行图片插入(不为空插入图片是矩形不是像微信那种圆弧矩形)
*
* @param content 二维码内容
* @param logoPath 图片地址
* @param needCompress 是否压缩
* @return
* @throws Exception
*/
private static BufferedImage createImage(String content, String logoPath, boolean needCompress) throws Exception {
Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
//这里选择H也就是大的错误纠正级别,也就是二维码的信息变多,M和H就是明显的对比
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, UNICODE);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QR_CODE_WIDTH, QR_CODE_HEIGHT, hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
//黑白配颜色(0xFF000000(二维码颜色,黑色--问好后第一个参数)||0xFFFFFFFF(白色))
image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
}
}
//如果logo图片不存在则不去执行下面的插入图片操作
if (logoPath == null || "".equals(logoPath)) {
return image;
}
// 插入图片
SimpleQrCodeUtils.insertImage(image, logoPath, needCompress);
return image;
}
/**
* 为二维码添加logo图片(圆弧矩形)---微信就是这种圆弧型的(允许同包下访问)
*
* @param matrixImage 源二维码图片
* @return 返回带有logo的二维码图片
* @throws IOException
* @author Administrator sangwenhao
*/
protected static BufferedImage logoMatrix(BufferedImage matrixImage, String logoPath) throws IOException {
//读取二维码图片,并构建绘图对象
Graphics2D g2 = matrixImage.createGraphics();
//获取图的宽高信息
int matrixWidth = matrixImage.getWidth();
int matrixHeight = matrixImage.getHeight();
//读取Logo图片
//获取指定目录文件(这个是打成jar后获取的方式,如果是在resources下,直接使用对应路径即可,例如/static/a.png)
ClassPathResource resource = new ClassPathResource(logoPath);
//读取图片文件到图片缓冲区
BufferedImage logo = ImageIO.read(resource.getInputStream());
//开始绘制图片
g2.drawImage(logo, matrixWidth / 5 * 2, matrixHeight / 5 * 2, matrixWidth / 5, matrixHeight / 5, null);
BasicStroke stroke = new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
// 设置笔画对象
g2.setStroke(stroke);
//指定弧度的圆角矩形
RoundRectangle2D.Float round = new RoundRectangle2D.Float(matrixWidth / 5 * 2, matrixHeight / 5 * 2, matrixWidth / 5, matrixHeight / 5, 20, 20);
g2.setColor(Color.white);
// 绘制圆弧矩形
g2.draw(round);
//设置logo 有一道灰色边框
BasicStroke stroke2 = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
// 设置笔画对象
g2.setStroke(stroke2);
RoundRectangle2D.Float round2 = new RoundRectangle2D.Float(matrixWidth / 5 * 2 + 2, matrixHeight / 5 * 2 + 2, matrixWidth / 5 - 4, matrixHeight / 5 - 4, 20, 20);
g2.setColor(new Color(128, 128, 128));
// 绘制圆弧矩形
g2.draw(round2);
g2.dispose();
matrixImage.flush();
return matrixImage;
}
/**
* 插入LOGO
*
* @param source 二维码图片
* @param logoPath LOGO图片地址
* @param needCompress 是否压缩
* @throws Exception
*/
private static void insertImage(BufferedImage source, String logoPath, boolean needCompress) throws Exception {
//获取指定目录文件(这个是打成jar后获取的方式,如果是在resources下,直接使用对应路径即可,例如/static/a.png)
ClassPathResource resource = new ClassPathResource(logoPath);
//读取图片文件到图片缓冲区(只有获取输入流的方式不会有问题,getFile在jar的情况有问题)
Image src = ImageIO.read(resource.getInputStream());
//不使用new File()的方式,是因为(jar情况下)路径问题
int width = src.getWidth(null);
int height = src.getHeight(null);
// 压缩LOGO
if (needCompress) {
if (width > LOGO_WIDTH) {
width = LOGO_WIDTH;
}
if (height > LOGO_HEIGHT) {
height = LOGO_HEIGHT;
}
Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
BufferedImage tag = new BufferedImage(width, height, 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 = (QR_CODE_WIDTH - width) / 2;
int y = (QR_CODE_HEIGHT - height) / 2;
graph.drawImage(src, x, y, width, height, null);
Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
graph.setStroke(new BasicStroke(3f));
graph.draw(shape);
graph.dispose();
}
/**
* 生成二维码(内嵌LOGO)
* 调用者指定二维码文件名
*
* @param content 二维码的内容
* @param logoPath 中间图片地址
* @param destPath 存储路径
* @param fileName 文件名称
* @param needCompress 是否压缩
* @return
* @throws Exception
*/
public static String getQrCode(String content, String logoPath, String destPath, String fileName, boolean needCompress) throws Exception {
BufferedImage image = SimpleQrCodeUtils.createImage(content, logoPath, needCompress);
mkdirs(destPath);
//文件名称通过传递
fileName = fileName.substring(0, fileName.indexOf(".") > 0 ? fileName.indexOf(".") : fileName.length())
+ "." + FORMAT.toLowerCase();
ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName));
return fileName;
}
/**
* 生成二维码(内嵌LOGO)
* 调用者指定二维码文件名
*
* @param content 二维码的内容
* @param logoPath 中间图片地址
* @param needCompress 是否压缩
* @return
*/
public static BufferedImage getQrCode(String content, String logoPath, boolean needCompress) throws Exception {
//获取缓冲区图像对象(如果logoPath传空过去,则返回的是没有加入logo的BufferedImage(矩形的logo图))
BufferedImage bufferedImage = SimpleQrCodeUtils.createImage(content, logoPath, needCompress);
return bufferedImage;
}
/**
* 生成二维码(内嵌LOGO)
* 调用者指定二维码文件名
*
* @param content 二维码的内容
* @param logoPath 中间图片地址
* @param needCompress 是否压缩
* @return
*/
public static InputStream getQrCodeInputStream(String content, String logoPath, boolean needCompress) throws Exception {
//获取缓冲区图像对象(如果logoPath传空过去,则返回的是没有加入logo的BufferedImage(矩形的logo图))
BufferedImage bufferedImage = SimpleQrCodeUtils.createImage(content, logoPath, needCompress);
return ColourQrCodeUtils.getInputStream(bufferedImage);
}
/**
* 生成二维码(内嵌LOGO)
* 调用者指定二维码文件名
*
* @param content 二维码的内容
* @param logoPath 中间图片地址
* @param needCompress 是否压缩
* @return
*/
public static BufferedImage getQrCodeAsWeChat(String content, String logoPath, boolean needCompress) throws Exception {
//获取缓冲区图像对象(如果logoPath传空过去,则返回的是没有加入logo的BufferedImage(矩形的logo图))
BufferedImage bufferedImage = SimpleQrCodeUtils.createImage(content, null, needCompress);
//调用圆弧矩形插入logo的方式插入图片
return logoMatrix(bufferedImage, logoPath);
}
/**
* 生成二维码(内嵌LOGO)
* 调用者指定二维码文件名
*
* @param content 二维码的内容
* @param widthHeight 二维码的宽和高(生成正方形的二维码,所以宽高相同)
* @param logoPath 中间图片地址
* @param needCompress 是否压缩
* @return
*/
public static BufferedImage getQrCodeAsWeChat(String content, int widthHeight, String logoPath, boolean needCompress) throws Exception {
//调用创建图片之前先修改二维码的默认宽高
QR_CODE_WIDTH = widthHeight;
//获取缓冲区图像对象(如果logoPath传空过去,则返回的是没有加入logo的BufferedImage(矩形的logo图))
BufferedImage bufferedImage = SimpleQrCodeUtils.createImage(content, null, needCompress);
//调用圆弧矩形插入logo的方式插入图片
return logoMatrix(bufferedImage, logoPath);
}
/**
* 创建文件夹, mkdirs会自动创建多层目录,区别于mkdir.(mkdir如果父目录不存在则会抛出异常)
*
* @param destPath
*/
private static void mkdirs(String destPath) {
File file = new File(destPath);
if (!file.exists() && !file.isDirectory()) {
file.mkdirs();
}
}
/**
* 解析二维码(实测微信扫描可解码内容,如果是网址,则直接跳转到对应网站)
*
* @param path 二维码图片路径
* @return String 二维码内容
* @throws Exception
*/
public static String decodeQrCode(String path) throws Exception {
File file = new File(path);
BufferedImage image = ImageIO.read(file);
if (Objects.isNull(image)) {
return null;
}
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Hashtable<DecodeHintType, Object> hints = new Hashtable<>();
hints.put(DecodeHintType.CHARACTER_SET, UNICODE);
Result result = new MultiFormatReader().decode(bitmap, hints);
return result.getText();
}
}
工具类有耦合,建议都加上使用,有些方法还没有测试过,具体实际情况看