Parcourir la source

增加tools-pdf,tools-zxing,tools-freemarker三个模块;ByteArray增加新方法;ZipReader和ZipWriter修改package位置

Signed-off-by: luoshi <luoshi@qmth.com.cn>
luoshi il y a 1 an
Parent
commit
4a1126c004

+ 4 - 6
core-solar/src/main/java/com/qmth/boot/core/solar/crypto/AppLicenseUtil.java

@@ -29,9 +29,8 @@ public class AppLicenseUtil {
      * @return
      */
     public static ByteArray buildDeviceInfo() throws Exception {
-        return RSA.getPublicKey(
-                ByteArray.fromInputStream(AppLicenseUtil.class.getClassLoader().getResourceAsStream(PUBLIC_KEY_PATH))
-                        .value()).encrypt(new ObjectMapper().writeValueAsBytes(DeviceInfo.current()));
+        return RSA.getPublicKey(ByteArray.fromResource(PUBLIC_KEY_PATH).value())
+                .encrypt(new ObjectMapper().writeValueAsBytes(DeviceInfo.current()));
     }
 
     /**
@@ -93,9 +92,8 @@ public class AppLicenseUtil {
             //截取前16位摘要信息
             byte[] digest = ArrayUtils.subarray(original, 0, 16);
             //进行RSA解密
-            byte[] content = RSA.getPublicKey(ByteArray
-                    .fromInputStream(AppLicenseUtil.class.getClassLoader().getResourceAsStream(PUBLIC_KEY_PATH))
-                    .value()).decrypt(ArrayUtils.subarray(original, 16, original.length)).value();
+            byte[] content = RSA.getPublicKey(ByteArray.fromResource(PUBLIC_KEY_PATH).value())
+                    .decrypt(ArrayUtils.subarray(original, 16, original.length)).value();
             //比对摘要是否一致
             if (Arrays.equals(digest, CodecUtils.md5(content))) {
                 return new ObjectMapper().readValue(content, AppLicense.class);

+ 50 - 0
pom.xml

@@ -15,6 +15,9 @@
         <module>tools-signature</module>
         <module>tools-common</module>
         <module>tools-device</module>
+        <module>tools-pdf</module>
+        <module>tools-freemarker</module>
+        <module>tools-zxing</module>
         <module>data-redis</module>
         <module>data-mybatis-plus</module>
         <module>core-models</module>
@@ -40,6 +43,8 @@
         <springboot.version>2.3.7.RELEASE</springboot.version>
         <okhttp.version>3.14.9</okhttp.version>
         <io.micrometer.version>1.10.4</io.micrometer.version>
+        <openpdf.version>1.3.33</openpdf.version>
+        <zxing.version>3.5.2</zxing.version>
     </properties>
 
     <dependencyManagement>
@@ -65,6 +70,21 @@
                 <artifactId>tools-device</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.qmth.boot</groupId>
+                <artifactId>tools-pdf</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.qmth.boot</groupId>
+                <artifactId>tools-freemarker</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.qmth.boot</groupId>
+                <artifactId>tools-zxing</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>com.qmth.boot</groupId>
                 <artifactId>tools-common</artifactId>
@@ -352,6 +372,36 @@
                 <artifactId>slf4j-api</artifactId>
                 <version>1.7.32</version>
             </dependency>
+            <!-- https://mvnrepository.com/artifact/com.github.librepdf/openpdf -->
+            <dependency>
+                <groupId>com.github.librepdf</groupId>
+                <artifactId>openpdf</artifactId>
+                <version>${openpdf.version}</version>
+            </dependency>
+            <!-- https://mvnrepository.com/artifact/com.github.librepdf/openpdf-fonts-extra -->
+            <dependency>
+                <groupId>com.github.librepdf</groupId>
+                <artifactId>openpdf-fonts-extra</artifactId>
+                <version>${openpdf.version}</version>
+            </dependency>
+            <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
+            <dependency>
+                <groupId>org.freemarker</groupId>
+                <artifactId>freemarker</artifactId>
+                <version>2.3.32</version>
+            </dependency>
+            <!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
+            <dependency>
+                <groupId>com.google.zxing</groupId>
+                <artifactId>core</artifactId>
+                <version>${zxing.version}</version>
+            </dependency>
+            <!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
+            <dependency>
+                <groupId>com.google.zxing</groupId>
+                <artifactId>javase</artifactId>
+                <version>${zxing.version}</version>
+            </dependency>
             <dependency>
                 <groupId>junit</groupId>
                 <artifactId>junit</artifactId>

+ 28 - 0
tools-common/src/main/java/com/qmth/boot/tools/models/ByteArray.java

@@ -30,6 +30,21 @@ public class ByteArray {
         return value;
     }
 
+    /**
+     * 根据classpath路径初始化,路径不需要"classpath:"前缀,且不能以/开头
+     *
+     * @param path 从classpath中寻找的资源文件路径
+     * @return
+     */
+    public static ByteArray fromResource(String path) throws IOException {
+        InputStream ins = ByteArray.class.getClassLoader().getResourceAsStream(path);
+        try {
+            return ByteArray.fromInputStream(ins);
+        } finally {
+            IOUtils.closeQuietly(ins);
+        }
+    }
+
     /**
      * 根据URL初始化
      *
@@ -205,6 +220,19 @@ public class ByteArray {
         return CodecUtils.toBase64(value);
     }
 
+    /**
+     * 根据byte[]构造输入流
+     *
+     * @return
+     */
+    public ByteArrayInputStream toInputStream() {
+        if (value != null) {
+            return new ByteArrayInputStream(value);
+        } else {
+            return null;
+        }
+    }
+
     /**
      * byte[]到输出流
      *

+ 2 - 1
tools-common/src/main/java/com/qmth/boot/tools/io/ZipReader.java → tools-common/src/main/java/com/qmth/boot/tools/zip/ZipReader.java

@@ -1,5 +1,6 @@
-package com.qmth.boot.tools.io;
+package com.qmth.boot.tools.zip;
 
+import com.qmth.boot.tools.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 
 import java.io.File;

+ 2 - 1
tools-common/src/main/java/com/qmth/boot/tools/io/ZipWriter.java → tools-common/src/main/java/com/qmth/boot/tools/zip/ZipWriter.java

@@ -1,5 +1,6 @@
-package com.qmth.boot.tools.io;
+package com.qmth.boot.tools.zip;
 
+import com.qmth.boot.tools.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 
 import java.io.*;

+ 1 - 1
tools-common/src/test/java/com/qmth/boot/test/tools/zip/ZipWriterTest.java

@@ -1,6 +1,6 @@
 package com.qmth.boot.test.tools.zip;
 
-import com.qmth.boot.tools.io.ZipWriter;
+import com.qmth.boot.tools.zip.ZipWriter;
 
 import java.io.File;
 import java.io.IOException;

+ 30 - 0
tools-freemarker/pom.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>qmth-boot</artifactId>
+        <groupId>com.qmth.boot</groupId>
+        <version>1.0.4</version>
+    </parent>
+    <artifactId>tools-freemarker</artifactId>
+    <name>tools-freemarker</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.qmth.boot</groupId>
+            <artifactId>tools-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 67 - 0
tools-freemarker/src/main/java/com/qmth/boot/tools/freemarker/FreemarkerUtil.java

@@ -0,0 +1,67 @@
+package com.qmth.boot.tools.freemarker;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateExceptionHandler;
+
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.WeakHashMap;
+
+/**
+ * Freemarker模版工具
+ */
+public class FreemarkerUtil {
+
+    private static Configuration configuration;
+
+    private static WeakHashMap<String, Template> templateMap = new WeakHashMap<>();
+
+    private static Configuration getConfiguration() {
+        if (configuration == null) {
+            configuration = new Configuration(Configuration.VERSION_2_3_32);
+            configuration.setDefaultEncoding(StandardCharsets.UTF_8.name());
+            configuration.setLogTemplateExceptions(false);
+            configuration.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
+        }
+        return configuration;
+    }
+
+    /**
+     * 使用数据对象按表达式取值
+     *
+     * @param expression freemarker格式表达式,如${key}
+     * @param data       数据对象
+     * @return
+     */
+    public static String getValue(String expression, Object data) {
+        return getValue(expression, data, null);
+    }
+
+    /**
+     * 使用数据对象按表达式取值
+     *
+     * @param expression freemarker格式表达式,如${key}
+     * @param data       数据对象
+     * @param fallback   异常返回值,为null则抛出过程异常
+     * @return
+     */
+    public static String getValue(String expression, Object data, String fallback) {
+        try {
+            Template template = templateMap.get(expression);
+            if (template == null) {
+                template = new Template(expression, expression, getConfiguration());
+                templateMap.put(expression, template);
+            }
+            StringWriter writer = new StringWriter();
+            template.process(data, writer);
+            return writer.toString();
+        } catch (Exception e) {
+            if (fallback != null) {
+                return fallback;
+            } else {
+                throw new RuntimeException("Freemarker value generate error", e);
+            }
+        }
+    }
+}

+ 35 - 0
tools-freemarker/src/test/java/com/qmth/boot/test/tools/freemarker/FreemarkerUtilTest.java

@@ -0,0 +1,35 @@
+package com.qmth.boot.test.tools.freemarker;
+
+import com.qmth.boot.tools.freemarker.FreemarkerUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class FreemarkerUtilTest {
+
+    @Test
+    public void test() {
+        Map<String, Object> map = new HashMap<>();
+        map.put("name", "ABC");
+        map.put("address", new HashMap<String, Object>() {{
+            put("city", "wuhan");
+        }});
+        map.put("child", new ArrayList<Object>() {{
+            add(new HashMap<String, Object>() {{
+                put("name", "c1");
+                put("age", 3);
+            }});
+            add(new HashMap<String, Object>() {{
+                put("name", "c2");
+                put("age", 4);
+            }});
+        }});
+        Assert.assertEquals("ABC", FreemarkerUtil.getValue("${name}", map));
+        Assert.assertEquals("wuhan", FreemarkerUtil.getValue("${address.city}", map));
+        Assert.assertEquals("4", FreemarkerUtil.getValue("${child[1].age}", map));
+        Assert.assertEquals("", FreemarkerUtil.getValue("${child[3].name}", map, ""));
+    }
+}

+ 43 - 0
tools-pdf/pom.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>qmth-boot</artifactId>
+        <groupId>com.qmth.boot</groupId>
+        <version>1.0.4</version>
+    </parent>
+    <artifactId>tools-pdf</artifactId>
+    <name>tools-pdf</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.qmth.boot</groupId>
+            <artifactId>tools-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.qmth.boot</groupId>
+            <artifactId>tools-freemarker</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.librepdf</groupId>
+            <artifactId>openpdf</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.librepdf</groupId>
+            <artifactId>openpdf-fonts-extra</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.qmth.boot</groupId>
+            <artifactId>tools-zxing</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 1 - 1
tools-common/src/main/java/com/qmth/boot/tools/html2pdf/HtmlToPdf.java → tools-pdf/src/main/java/com/qmth/boot/tools/pdf/HtmlToPdf.java

@@ -1,4 +1,4 @@
-package com.qmth.boot.tools.html2pdf;
+package com.qmth.boot.tools.pdf;
 
 import com.qmth.boot.tools.models.PaperSize;
 import org.apache.commons.lang3.StringUtils;

+ 97 - 0
tools-pdf/src/main/java/com/qmth/boot/tools/pdf/PdfFormUtil.java

@@ -0,0 +1,97 @@
+package com.qmth.boot.tools.pdf;
+
+import com.lowagie.text.Image;
+import com.lowagie.text.pdf.*;
+import com.qmth.boot.tools.freemarker.FreemarkerUtil;
+import com.qmth.boot.tools.io.IOUtils;
+import com.qmth.boot.tools.models.ByteArray;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+/**
+ * PDF表单填充工具
+ */
+public class PdfFormUtil {
+
+    private static BaseFont baseFont;
+
+    private static void fill(PdfReader reader, OutputStream ous, Object params) throws IOException {
+        // 加载默认字体
+        if (baseFont == null) {
+            baseFont = BaseFont.createFont("simsun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true,
+                    ByteArray.fromResource("font/simsun.ttf").value(), null);
+        }
+        PdfStamper stamp = new PdfStamper(reader, ous);
+        AcroFields form = stamp.getAcroFields();
+        // 遍历表单字段
+        for (String key : form.getAllFields().keySet()) {
+            boolean image = form.getFieldType(key) == 1;
+            String param = getParam(key, params);
+            // 替换图片变量区内容
+            if (image && param.length() > 0) {
+                int size = form.getFieldItem(key) != null ? form.getFieldItem(key).size() : 0;
+                for (int i = 0; i < size; i++) {
+                    PushbuttonField field = form.getNewPushbuttonFromField(key, i);
+                    field.setLayout(PushbuttonField.LAYOUT_ICON_ONLY);
+                    field.setProportionalIcon(true);
+                    field.setImage(Image.getInstance(ByteArray.fromBase64(param).value()));
+                    form.replacePushbuttonField(key, field.getField(), i);
+                }
+            }
+            // 替换文字变量区内容
+            else if (!image) {
+                form.setFieldProperty(key, "textfont", baseFont, null);
+                form.setField(key, param);
+            }
+        }
+        stamp.setFormFlattening(true);
+        stamp.close();
+        reader.close();
+        IOUtils.closeQuietly(ous);
+    }
+
+    private static String getParam(String key, Object params) {
+        // 使用FreeMarker表达式获取变量值
+        if (key.startsWith("${")) {
+            return FreemarkerUtil.getValue(key, params, "");
+        }
+        // 非ft表达式,且数据对象为map时通过key获取变量值
+        else if (params instanceof Map) {
+            Object param = ((Map) params).get(key);
+            return param != null ? param.toString() : "";
+        }
+        // 默认返回
+        else {
+            return "";
+        }
+    }
+
+    /**
+     * <p>PDF表单填充,生成新的PDF文件</p>
+     * <p>图片需要转成base64字符串放置到数据对象中</p>
+     *
+     * @param ins    PDF模版文件输入流
+     * @param ous    新PDF文件输出流
+     * @param params 数据对象
+     * @throws IOException
+     */
+    public static void fill(InputStream ins, OutputStream ous, Object params) throws IOException {
+        fill(new PdfReader(ins), ous, params);
+    }
+
+    /**
+     * <p>PDF表单填充,生成新的PDF文件</p>
+     * <p>图片需要转成base64字符串放置到数据对象中</p>
+     *
+     * @param data   PDF模版文件内容
+     * @param ous    新PDF文件输出流
+     * @param params 数据对象
+     * @throws IOException
+     */
+    public static void fill(byte[] data, OutputStream ous, Object params) throws IOException {
+        fill(new PdfReader(data), ous, params);
+    }
+}

+ 91 - 0
tools-pdf/src/main/java/com/qmth/boot/tools/pdf/PdfMergeFile.java

@@ -0,0 +1,91 @@
+package com.qmth.boot.tools.pdf;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.pdf.PdfImportedPage;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfSmartCopy;
+import com.qmth.boot.tools.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * PDF文档合并工具
+ */
+public class PdfMergeFile {
+
+    private Document document;
+
+    private PdfSmartCopy copy;
+
+    private OutputStream ous;
+
+    /**
+     * 初始化合并文档,并指定输出流
+     *
+     * @param ous 输出数据流
+     */
+    public PdfMergeFile(OutputStream ous) {
+        this.ous = ous;
+    }
+
+    /**
+     * 将指定PDF文件合并到当前文档
+     *
+     * @param ins      待合并PDF文件输入流
+     * @param autoFill 是否自动补齐偶数页
+     * @return 当前实例用于链式调用
+     * @throws IOException
+     */
+    public PdfMergeFile merge(InputStream ins, boolean autoFill) throws IOException {
+        return merge(new PdfReader(ins), autoFill);
+    }
+
+    /**
+     * 将指定PDF文件合并到当前文档
+     *
+     * @param data     待合并PDF文件内容
+     * @param autoFill 是否自动补齐偶数页
+     * @return 当前实例用于链式调用
+     * @throws IOException
+     */
+    public PdfMergeFile merge(byte[] data, boolean autoFill) throws IOException {
+        return merge(new PdfReader(data), autoFill);
+    }
+
+    private PdfMergeFile merge(PdfReader reader, boolean autoFill) throws IOException {
+        // 根据首个pdf文件初始化大小
+        if (document == null) {
+            Rectangle rect = reader.getPageSize(1);
+            document = new Document(rect);
+            copy = new PdfSmartCopy(document, ous);
+            document.open();
+        }
+        // 逐页复制
+        int n = reader.getNumberOfPages();
+        for (int i = 1; i <= n; i++) {
+            document.newPage();
+            PdfImportedPage page = copy.getImportedPage(reader, i);
+            copy.addPage(page);
+        }
+        // 奇数页自动补齐为偶数
+        if (autoFill && n % 2 != 0) {
+            document.newPage();
+            copy.addPage(reader.getPageSize(1), reader.getPageRotation(1));
+        }
+        reader.close();
+        return this;
+    }
+
+    /**
+     * 结束合并,关闭当前文档与输出流
+     */
+    public void close() {
+        if (document != null) {
+            document.close();
+        }
+        IOUtils.closeQuietly(ous);
+    }
+}

BIN
tools-pdf/src/main/resources/font/simsun.ttf


+ 28 - 0
tools-pdf/src/test/java/com/qmth/boot/test/tools/pdf/PdfFormUtilTest.java

@@ -0,0 +1,28 @@
+package com.qmth.boot.test.tools.pdf;
+
+import com.google.zxing.WriterException;
+import com.qmth.boot.tools.models.ByteArray;
+import com.qmth.boot.tools.pdf.PdfFormUtil;
+import com.qmth.boot.tools.zxing.BarcodeEncoder;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class PdfFormUtilTest {
+
+    @Test
+    public void test() throws IOException, WriterException {
+        ByteArray template = ByteArray.fromResource("template.pdf");
+        ByteArrayOutputStream ous = new ByteArrayOutputStream();
+        Map<String, Object> params = new HashMap<>();
+        params.put("package_code", "1001");
+        params.put("campus", "武汉学习中心");
+        params.put("barcode", BarcodeEncoder.encode("1001", 400, 100).toBase64());
+        PdfFormUtil.fill(template.value(), ous, params);
+        Assert.assertTrue(ous.toByteArray().length > 0);
+    }
+}

+ 25 - 0
tools-pdf/src/test/java/com/qmth/boot/test/tools/pdf/PdfMergeTest.java

@@ -0,0 +1,25 @@
+package com.qmth.boot.test.tools.pdf;
+
+import com.lowagie.text.pdf.PdfReader;
+import com.qmth.boot.tools.models.ByteArray;
+import com.qmth.boot.tools.pdf.PdfMergeFile;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class PdfMergeTest {
+
+    @Test
+    public void test() throws IOException {
+        ByteArrayOutputStream ous = new ByteArrayOutputStream();
+        PdfMergeFile merge = new PdfMergeFile(ous);
+        merge.merge(ByteArray.fromResource("template.pdf").value(), true)
+                .merge(ByteArray.fromResource("template.pdf").value(), false);
+        merge.close();
+        PdfReader reader = new PdfReader(ous.toByteArray());
+        Assert.assertEquals(3, reader.getNumberOfPages());
+        reader.close();
+    }
+}

BIN
tools-pdf/src/test/resources/template.pdf


+ 33 - 0
tools-zxing/pom.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>qmth-boot</artifactId>
+        <groupId>com.qmth.boot</groupId>
+        <version>1.0.4</version>
+    </parent>
+    <artifactId>tools-zxing</artifactId>
+    <name>tools-zxing</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.qmth.boot</groupId>
+            <artifactId>tools-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>javase</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>

+ 49 - 0
tools-zxing/src/main/java/com/qmth/boot/tools/zxing/BarcodeDecoder.java

@@ -0,0 +1,49 @@
+package com.qmth.boot.tools.zxing;
+
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.MultiFormatReader;
+import com.google.zxing.Result;
+import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
+import com.google.zxing.common.HybridBinarizer;
+
+import javax.imageio.ImageIO;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 条码图片解码工具
+ */
+public class BarcodeDecoder {
+
+    private static Map<DecodeHintType, Object> hints = new HashMap<DecodeHintType, Object>() {{
+        // 指定深度尝试
+        put(DecodeHintType.TRY_HARDER, true);
+        // 指定编码格式
+        put(DecodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
+    }};
+
+    /**
+     * 识别条码内容
+     *
+     * @param ins      图片输入流
+     * @param fallback 异常返回值,为null则抛出过程异常
+     * @return 识别结果
+     */
+    public static String decode(InputStream ins, String fallback) {
+        try {
+            BinaryBitmap binaryBitmap = new BinaryBitmap(
+                    new HybridBinarizer(new BufferedImageLuminanceSource(ImageIO.read(ins))));
+            Result result = new MultiFormatReader().decode(binaryBitmap, hints);
+            return result.getText();
+        } catch (Exception e) {
+            if (fallback != null) {
+                return fallback;
+            } else {
+                throw new RuntimeException("Barcode decode error", e);
+            }
+        }
+    }
+}

+ 48 - 0
tools-zxing/src/main/java/com/qmth/boot/tools/zxing/BarcodeEncoder.java

@@ -0,0 +1,48 @@
+package com.qmth.boot.tools.zxing;
+
+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.qrcode.decoder.ErrorCorrectionLevel;
+import com.qmth.boot.tools.models.ByteArray;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 条码图片生成工具
+ */
+public class BarcodeEncoder {
+
+    private static final String DEFAULT_IMAGE_TYPE = "png";
+
+    private static Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>() {{
+        // 指定纠错等级
+        put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+        // 指定编码格式
+        put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
+    }};
+
+    /**
+     * 根据指定内容生成条码图片,默认格式为PNG
+     *
+     * @param content 条码内容
+     * @param width   图片长度
+     * @param height  图片高度
+     * @return 图片数据流
+     * @throws WriterException
+     * @throws IOException
+     */
+    public static ByteArray encode(String content, int width, int height) throws WriterException, IOException {
+        ByteArrayOutputStream ous = new ByteArrayOutputStream();
+        BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.CODE_128, width, height, hints);
+        MatrixToImageWriter.writeToStream(bitMatrix, DEFAULT_IMAGE_TYPE, ous);
+        return ByteArray.fromArray(ous.toByteArray());
+    }
+}

+ 20 - 0
tools-zxing/src/test/java/com/qmth/boot/test/tools/zxing/BarcodeTest.java

@@ -0,0 +1,20 @@
+package com.qmth.boot.test.tools.zxing;
+
+import com.google.zxing.WriterException;
+import com.qmth.boot.tools.zxing.BarcodeDecoder;
+import com.qmth.boot.tools.zxing.BarcodeEncoder;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class BarcodeTest {
+
+    @Test
+    public void test() throws IOException, WriterException {
+        String content = RandomStringUtils.random(16, true, true);
+        Assert.assertEquals(content,
+                BarcodeDecoder.decode(BarcodeEncoder.encode(content, 400, 100).toInputStream(), null));
+    }
+}