前景:由于公司需求一个工具,这个工具需要将csv文件中的数据存入二维码中,使用手机进行识别。
本文采用opencsv和google的zxing来将csv中的数据存储到二维码中,但是最终结果发现一个问题,明文存储在二维码中,将不能够存很多数据,因此,想了一个办法,便是使用GZIP对内容进行压缩后再存入二维码中。
本文使用的是maven项目,bean目录存储需要将数据转换成的实体,util目录就是各个工具类,main目录主要是为了测试生成二维码并解压字符串。
这里准备了7条数据,包括姓名、性别、年龄。
这里依赖包括,fastjson:用于将实体转换成json字符串,opencsv:读取csv文件所必需的,zxing:谷歌生成二维码的依赖,这里有一个坑:需要去集成lombok依赖,否则无法将读取的数据转换成对应实体的集合。
com.alibaba
fastjson
1.2.76
com.opencsv
opencsv
5.5.2
org.projectlombok
lombok
1.18.12
com.google.zxing
core
3.4.1
com.google.zxing
javase
3.4.1
这里以人为例,人包括属性姓名、性别、年龄等,这里使用@CsvBindByName来与csv中的表格头部对应。
@Data
public class People {
/**
* 姓名
*/
@CsvBindByName(column = "姓名")
private String name;
/**
* 性别
*/
@CsvBindByName(column = "性别")
private String sex;
/**
* 年龄
*/
@CsvBindByName(column = "年龄")
private Integer age;
}
这是读取csv文件的工具类,我将它封装成了一个泛型函数,其中fileName是文件名称路径(应该命名成filePath的,懒得改啦!!!),clazz就是需要将csv转换成所需实体。
/**
* opencsv工具类
*/
public class OpencsvUtil {
/**
* 读取csv并转换成相应的bean
*
* @param fileName 文件名称路径
* @param clazz 最终获取到的bean
* @param 泛型
*/
public static List readCsvByNameToBean(String fileName, Class clazz) throws FileNotFoundException {
// 定义流
CsvToBean csvToBean = null;
try {
InputStreamReader in = new InputStreamReader(new FileInputStream(fileName), StandardCharsets.UTF_8);
HeaderColumnNameMappingStrategy mappingStrategy = new HeaderColumnNameMappingStrategy<>();
// 设置bean类型
mappingStrategy.setType(clazz);
CsvToBeanBuilder build = new CsvToBeanBuilder(in);
build.withMappingStrategy(mappingStrategy);
// 设置分隔符
build.withSeparator(CSVWriter.DEFAULT_SEPARATOR);
csvToBean = build.build();
} catch (IOException e) {
e.printStackTrace();
}
return csvToBean == null ? null : csvToBean.parse();
}
}
这个是生成二维码工具,可以根据需要手动设置二维码尺寸以及logo的宽度和高度。
/**
* 二维码工具类
*/
public class QRcodeUtil {
private static final String CHARSET = "utf-8";
// 二维码尺寸
private static final int QRCODE_SIZE = 500;
// LOGO宽度
private static final int LOGO_WIDTH = 128;
// LOGO高度
private static final int LOGO_HEIGHT = 29;
/**
* 创建二维码
*
* @param content 二维码的内容
*/
public static BufferedImage createImage(String content) {
Hashtable hints = new Hashtable();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = null;
try {
bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,
hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
}
}
return image;
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}
/**
* 插入LOGO
*
* @param source 二维码图片
* @param logoPath LOGO图片地址
*/
public static void insertImage(BufferedImage source, InputStream logoPath) {
Image src = null;
try {
src = ImageIO.read(logoPath);
// 插入LOGO
Graphics2D graph = source.createGraphics();
int x = (QRCODE_SIZE - LOGO_WIDTH) / 2;
int y = (QRCODE_SIZE - LOGO_HEIGHT) / 2;
graph.drawImage(src, x, y, LOGO_WIDTH, LOGO_HEIGHT, null);
Shape shape = new RoundRectangle2D.Float(x, y, LOGO_WIDTH, LOGO_HEIGHT, 12, 12);
graph.setStroke(new BasicStroke(3f));
graph.draw(shape);
graph.dispose();
} catch (IOException e) {
e.printStackTrace();
}
}
}
由于二维码的存储内容有限,尤其是黑白二维码,存储的内容可以说是少之又少,因此可以使用GZIP对其进行压缩。
/**
* 压缩工具
*/
public class ZipUtil {
/**
* 使用gzip压缩字符串
*
* @param str 要压缩的字符串
*/
public static String compress(String str) {
if (str == null || str.length() == 0) {
return str;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip = null;
try {
gzip = new GZIPOutputStream(out);
gzip.write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (gzip != null) {
try {
gzip.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return new String(Base64.getEncoder().encode(out.toByteArray()));
}
/**
* 使用gzip解压缩
*
* @param compressedStr 压缩字符串
*/
public static String uncompress(String compressedStr) {
if (compressedStr == null) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = null;
GZIPInputStream ginzip = null;
byte[] compressed = null;
String decompressed = null;
try {
compressed = Base64.getDecoder().decode(compressedStr);
in = new ByteArrayInputStream(compressed);
ginzip = new GZIPInputStream(in);
byte[] buffer = new byte[1024];
int offset = -1;
while ((offset = ginzip.read(buffer)) != -1) {
out.write(buffer, 0, offset);
}
decompressed = out.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ginzip != null) {
try {
ginzip.close();
} catch (IOException ignored) {
}
}
if (in != null) {
try {
in.close();
} catch (IOException ignored) {
}
}
try {
out.close();
} catch (IOException ignored) {
}
}
return decompressed;
}
}
本文将二维码图片存储成临时文件,用户可以根据需求自行修改存储方式,在这里我定义了图片的后缀名,应该将后缀名作为一个参数传入的,这里懒得改啦!!!
/**
* 临时文件工具类
*/
public class TempFileUtil {
/**
* 保存图片
*
* @param imageName 图片名称
* @param bufferedImage 图片
* @param imageType 图片类型
*/
public static File createImageTempFile(String imageName, BufferedImage bufferedImage, String imageType) {
try {
File tempFile = File.createTempFile(imageName, ".png");
// 将图片写入临时文件
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, imageType, baos);
InputStream fis = new ByteArrayInputStream(baos.toByteArray());
OutputStream fos = new FileOutputStream(tempFile);
int len = 0;
byte[] bytes = new byte[1024];
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
// 关闭流
fis.close();
fos.close();
return tempFile;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
本文将路径存储于“C:\\Users\\1\\Desktop\\data\\data.csv”,用户只需修改文件路径即可。文件所含内容请看第二章的第一节。
public class CreateQRcode {
public static void main(String[] args) {
String fileName = "C:\\Users\\1\\Desktop\\data\\data.csv";
try {
// 将csv数据转换成对应的bean并过滤
List problemList = Objects.requireNonNull(OpencsvUtil.readCsvByNameToBean(fileName, People.class));
//打印数据
for (People people : problemList) {
System.out.println(JSONObject.toJSONString(people));
}
// 将bean存入二维码
String content = ZipUtil.compress(JSONObject.toJSONString(problemList));
BufferedImage image = QRcodeUtil.createImage(content);
if (image != null) {
File png = TempFileUtil.createImageTempFile(new File(fileName).getName().split("\\.")[0], image, "png");
assert png != null;
System.out.println("存放路径:" + png.getAbsolutePath());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
可见图片存放于:C:\Users\1\AppData\Local\Temp\data3809453543411187204.png
扫描二维码,可得识别结果:H4sIAAAAAAAAAIuuVkpMT1WyMrTQUSpOrVCyUno+ZbtSrQ5M2By7sAFM+OnSzUjCJBmCQ7EpitGxADCNnimhAAAA
/**
* @author anxin
* @date 2022/4/2 21:53
*/
public class UnzipData {
public static void main(String[] args) {
String compressData = "H4sIAAAAAAAAAIuuVkpMT1WyMrTQUSpOrVCyUno+ZbtSrQ5M2By7sAFM+OnSzUjCJBmCQ7EpitGxADCNnimhAAAA";
String uncompress = ZipUtil.uncompress(compressData);
System.out.println("解压字符串成功!");
System.out.println("---------------数据开始-------------");
List people = JSONObject.parseArray(uncompress, People.class);
for (People person : people) {
System.out.println(JSONObject.toJSONString(person));
}
System.out.println("---------------数据结束-------------");
}
}
对扫描结果进行gzip解压缩,可得到二维码存入的内容,结果如下: