diff --git a/CHANGELOG.md b/CHANGELOG.md index e7055c2ca..d442f4958 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,13 +3,15 @@ ------------------------------------------------------------------------------------------------------------- -## 5.3.6 (2020-05-17) +## 5.3.6 (2020-05-19) ### 新特性 * 【core 】 NumberConverter Long类型增加日期转换(pr#872@Github) * 【all 】 StrUtil and SymmetricCrypto注释修正(pr#873@Github) * 【core 】 CsvReader支持返回Bean(issue#869@Github) * 【core 】 Snowflake循环等待下一个时间时避免长时间循环,加入对时钟倒退的判断(pr#874@Github) +* 【extra 】 新增 QRCode base64 编码形式返回(pr#878@Github) +* 【core 】 ImgUtil增加toBase64DateUri,URLUtil增加getDataUri方法 ### Bug修复 diff --git a/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java b/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java index 21bbcdb61..74d0faaa1 100644 --- a/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/img/ImgUtil.java @@ -13,6 +13,7 @@ import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; import javax.imageio.IIOImage; import javax.imageio.ImageIO; @@ -1261,6 +1262,20 @@ public class ImgUtil { return IoUtil.toStream(toBytes(image, imageType)); } + /** + * 将图片对象转换为Base64的Data URI形式,格式为:data:image/[imageType];base64,[data] + * + * @param image 图片对象 + * @param imageType 图片类型 + * @return Base64的字符串表现形式 + * @since 5.3.6 + */ + public static String toBase64DateUri(Image image, String imageType) { + return URLUtil.getDataUri( + "image/" + imageType, "base64", + toBase64(image, imageType)); + } + /** * 将图片对象转换为Base64形式 * diff --git a/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java index 62359ef8a..989c89acb 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java @@ -28,7 +28,16 @@ import java.util.Map; import java.util.jar.JarFile; /** - * 统一资源定位符相关工具类 + * URL(Uniform Resource Locator)统一资源定位符相关工具类 + * + *

+ * 统一资源定位符,描述了一台特定服务器上某资源的特定位置。 + *

+ * URL组成: + *
+ *   协议://主机名[:端口]/ 路径/[:参数] [?查询]#Fragment
+ *   protocol :// hostname[:port] / path / [:parameters][?query]#fragment
+ * 
* * @author xiaoleilu */ @@ -749,11 +758,11 @@ public class URLUtil { * @throws IORuntimeException IO异常 * @since 5.3.4 */ - public static long getContentLength(URL url) throws IORuntimeException{ - if(null == url){ + public static long getContentLength(URL url) throws IORuntimeException { + if (null == url) { return -1; } - + URLConnection conn = null; try { conn = url.openConnection(); @@ -762,8 +771,61 @@ public class URLUtil { throw new IORuntimeException(e); } finally { if (conn instanceof HttpURLConnection) { - ((HttpURLConnection)conn).disconnect(); + ((HttpURLConnection) conn).disconnect(); } } } + + /** + * Data URI Scheme封装。data URI scheme 允许我们使用内联(inline-code)的方式在网页中包含数据,
+ * 目的是将一些小的数据,直接嵌入到网页中,从而不用再从外部文件载入。常用于将图片嵌入网页。 + * + *

+ * Data URI的格式规范: + *

+	 *     data:[][;charset=][;],
+	 * 
+ * + * @param mimeType 可选项(null表示无),数据类型(image/png、text/plain等) + * @param encoding 数据编码方式(US-ASCII,BASE64等) + * @param data 编码后的数据 + * @return Data URI字符串 + * @since 5.3.6 + */ + public static String getDataUri(String mimeType, String encoding, String data){ + return getDataUri(mimeType, null, encoding, data); + } + + /** + * Data URI Scheme封装。data URI scheme 允许我们使用内联(inline-code)的方式在网页中包含数据,
+ * 目的是将一些小的数据,直接嵌入到网页中,从而不用再从外部文件载入。常用于将图片嵌入网页。 + * + *

+ * Data URI的格式规范: + *

+	 *     data:[][;charset=][;],
+	 * 
+ * + * @param mimeType 可选项(null表示无),数据类型(image/png、text/plain等) + * @param charset 可选项(null表示无),源文本的字符集编码方式 + * @param encoding 数据编码方式(US-ASCII,BASE64等) + * @param data 编码后的数据 + * @return Data URI字符串 + * @since 5.3.6 + */ + public static String getDataUri(String mimeType, Charset charset, String encoding, String data){ + final StringBuilder builder = StrUtil.builder("data:"); + if(StrUtil.isNotBlank(mimeType)){ + builder.append(mimeType); + } + if(null != charset){ + builder.append(";charset=").append(charset.name()); + } + if(StrUtil.isNotBlank(encoding)){ + builder.append(';').append(encoding); + } + builder.append(',').append(data); + + return builder.toString(); + } } \ No newline at end of file diff --git a/hutool-extra/src/main/java/cn/hutool/extra/qrcode/QrCodeUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/qrcode/QrCodeUtil.java index 735f43766..f0cee3c74 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/qrcode/QrCodeUtil.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/qrcode/QrCodeUtil.java @@ -1,5 +1,6 @@ package cn.hutool.extra.qrcode; +import cn.hutool.core.codec.Base64; import cn.hutool.core.img.Img; import cn.hutool.core.img.ImgUtil; import cn.hutool.core.util.CharsetUtil; @@ -19,24 +20,17 @@ import com.google.zxing.common.HybridBinarizer; import java.awt.Image; import java.awt.Rectangle; import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.text.MessageFormat; -import java.util.Base64; import java.util.HashMap; -import javax.imageio.ImageIO; - /** * 基于Zxing的二维码工具类 - * + * * @author looly * @since 4.0.2 - * */ public class QrCodeUtil { @@ -50,13 +44,7 @@ public class QrCodeUtil { * @return 图片 Base64 编码字符串 */ public static String generateAsBase64(String content, QrConfig qrConfig, String imageType, String logoBase64) { - byte[] decode; - try { - decode = Base64.getDecoder().decode(logoBase64); - } catch (Exception e) { - throw new QrCodeException(e); - } - return generateAsBase64(content, qrConfig, imageType, decode); + return generateAsBase64(content, qrConfig, imageType, Base64.decode(logoBase64)); } /** @@ -69,36 +57,46 @@ public class QrCodeUtil { * @return 图片 Base64 编码字符串 */ public static String generateAsBase64(String content, QrConfig qrConfig, String imageType, byte[] logo) { - BufferedImage img; - try { - img = ImageIO.read(new ByteArrayInputStream(logo)); - } catch (IOException e) { - throw new QrCodeException(e); - } - qrConfig.setImg(img); + return generateAsBase64(content, qrConfig, imageType, ImgUtil.toImage(logo)); + } + + /** + * 生成代 logo 图片的 Base64 编码格式的二维码,以 String 形式表示 + * + * @param content 内容 + * @param qrConfig 二维码配置,包括长、宽、边距、颜色等 + * @param imageType 图片类型(图片扩展名),见{@link ImgUtil} + * @param logo logo 图片的byte[] + * @return 图片 Base64 编码字符串 + */ + public static String generateAsBase64(String content, QrConfig qrConfig, String imageType, Image logo) { + qrConfig.setImg(logo); return generateAsBase64(content, qrConfig, imageType); } /** * 生成 Base64 编码格式的二维码,以 String 形式表示 * + *

+ * 输出格式为: data:image/[type];base64,[data] + *

+ * * @param content 内容 * @param qrConfig 二维码配置,包括长、宽、边距、颜色等 * @param imageType 图片类型(图片扩展名),见{@link ImgUtil} * @return 图片 Base64 编码字符串 */ public static String generateAsBase64(String content, QrConfig qrConfig, String imageType) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - generate(content, qrConfig, imageType, bos); - byte[] encode = Base64.getEncoder().encode(bos.toByteArray()); - return MessageFormat.format("data:image/{0};base64,{1}", imageType, new String(encode)); + final BufferedImage img = generate(content, qrConfig); + return ImgUtil.toBase64DateUri(img, imageType); } + /** * 生成PNG格式的二维码图片,以byte[]形式表示 * * @param content 内容 - * @param width 宽度 - * @param height 高度 + * @param width 宽度 + * @param height 高度 * @return 图片的byte[] * @since 4.0.10 */ @@ -112,7 +110,7 @@ public class QrCodeUtil { * 生成PNG格式的二维码图片,以byte[]形式表示 * * @param content 内容 - * @param config 二维码配置,包括长、宽、边距、颜色等 + * @param config 二维码配置,包括长、宽、边距、颜色等 * @return 图片的byte[] * @since 4.1.2 */ @@ -124,10 +122,10 @@ public class QrCodeUtil { /** * 生成二维码到文件,二维码图片格式取决于文件的扩展名 - * - * @param content 文本内容 - * @param width 宽度 - * @param height 高度 + * + * @param content 文本内容 + * @param width 宽度 + * @param height 高度 * @param targetFile 目标文件,扩展名决定输出格式 * @return 目标文件 */ @@ -139,9 +137,9 @@ public class QrCodeUtil { /** * 生成二维码到文件,二维码图片格式取决于文件的扩展名 - * - * @param content 文本内容 - * @param config 二维码配置,包括长、宽、边距、颜色等 + * + * @param content 文本内容 + * @param config 二维码配置,包括长、宽、边距、颜色等 * @param targetFile 目标文件,扩展名决定输出格式 * @return 目标文件 * @since 4.1.2 @@ -154,12 +152,12 @@ public class QrCodeUtil { /** * 生成二维码到输出流 - * - * @param content 文本内容 - * @param width 宽度 - * @param height 高度 + * + * @param content 文本内容 + * @param width 宽度 + * @param height 高度 * @param imageType 图片类型(图片扩展名),见{@link ImgUtil} - * @param out 目标流 + * @param out 目标流 */ public static void generate(String content, int width, int height, String imageType, OutputStream out) { final BufferedImage image = generate(content, width, height); @@ -168,11 +166,11 @@ public class QrCodeUtil { /** * 生成二维码到输出流 - * - * @param content 文本内容 - * @param config 二维码配置,包括长、宽、边距、颜色等 + * + * @param content 文本内容 + * @param config 二维码配置,包括长、宽、边距、颜色等 * @param imageType 图片类型(图片扩展名),见{@link ImgUtil} - * @param out 目标流 + * @param out 目标流 * @since 4.1.2 */ public static void generate(String content, QrConfig config, String imageType, OutputStream out) { @@ -182,10 +180,10 @@ public class QrCodeUtil { /** * 生成二维码图片 - * + * * @param content 文本内容 - * @param width 宽度 - * @param height 高度 + * @param width 宽度 + * @param height 高度 * @return 二维码图片(黑白) */ public static BufferedImage generate(String content, int width, int height) { @@ -194,11 +192,11 @@ public class QrCodeUtil { /** * 生成二维码或条形码图片 - * + * * @param content 文本内容 - * @param format 格式,可选二维码或者条形码 - * @param width 宽度 - * @param height 高度 + * @param format 格式,可选二维码或者条形码 + * @param width 宽度 + * @param height 高度 * @return 二维码图片(黑白) */ public static BufferedImage generate(String content, BarcodeFormat format, int width, int height) { @@ -207,9 +205,9 @@ public class QrCodeUtil { /** * 生成二维码图片 - * + * * @param content 文本内容 - * @param config 二维码配置,包括长、宽、边距、颜色等 + * @param config 二维码配置,包括长、宽、边距、颜色等 * @return 二维码图片(黑白) * @since 4.1.2 */ @@ -220,10 +218,10 @@ public class QrCodeUtil { /** * 生成二维码或条形码图片
* 只有二维码时QrConfig中的图片才有效 - * + * * @param content 文本内容 - * @param format 格式,可选二维码、条形码等 - * @param config 二维码配置,包括长、宽、边距、颜色等 + * @param format 格式,可选二维码、条形码等 + * @param config 二维码配置,包括长、宽、边距、颜色等 * @return 二维码图片(黑白) * @since 4.1.14 */ @@ -256,12 +254,13 @@ public class QrCodeUtil { } // ------------------------------------------------------------------------------------------------------------------- encode + /** * 将文本内容编码为二维码 - * + * * @param content 文本内容 - * @param width 宽度 - * @param height 高度 + * @param width 宽度 + * @param height 高度 * @return {@link BitMatrix} */ public static BitMatrix encode(String content, int width, int height) { @@ -270,9 +269,9 @@ public class QrCodeUtil { /** * 将文本内容编码为二维码 - * + * * @param content 文本内容 - * @param config 二维码配置,包括长、宽、边距、颜色等 + * @param config 二维码配置,包括长、宽、边距、颜色等 * @return {@link BitMatrix} * @since 4.1.2 */ @@ -282,11 +281,11 @@ public class QrCodeUtil { /** * 将文本内容编码为条形码或二维码 - * + * * @param content 文本内容 - * @param format 格式枚举 - * @param width 宽度 - * @param height 高度 + * @param format 格式枚举 + * @param width 宽度 + * @param height 高度 * @return {@link BitMatrix} */ public static BitMatrix encode(String content, BarcodeFormat format, int width, int height) { @@ -295,10 +294,10 @@ public class QrCodeUtil { /** * 将文本内容编码为条形码或二维码 - * + * * @param content 文本内容 - * @param format 格式枚举 - * @param config 二维码配置,包括长、宽、边距、颜色等 + * @param format 格式枚举 + * @param config 二维码配置,包括长、宽、边距、颜色等 * @return {@link BitMatrix} * @since 4.1.2 */ @@ -319,9 +318,10 @@ public class QrCodeUtil { } // ------------------------------------------------------------------------------------------------------------------- decode + /** * 解码二维码图片为文本 - * + * * @param qrCodeInputstream 二维码输入流 * @return 解码文本 */ @@ -331,7 +331,7 @@ public class QrCodeUtil { /** * 解码二维码图片为文本 - * + * * @param qrCodeFile 二维码文件 * @return 解码文本 */ @@ -341,7 +341,7 @@ public class QrCodeUtil { /** * 将二维码图片解码为文本 - * + * * @param image {@link Image} 二维码图片 * @return 解码后的文本 */ @@ -351,9 +351,9 @@ public class QrCodeUtil { /** * 将二维码图片解码为文本 - * - * @param image {@link Image} 二维码图片 - * @param isTryHarder 是否优化精度 + * + * @param image {@link Image} 二维码图片 + * @param isTryHarder 是否优化精度 * @param isPureBarcode 是否使用复杂模式,扫描带logo的二维码设为true * @return 解码后的文本 * @since 4.3.1 @@ -389,8 +389,8 @@ public class QrCodeUtil { /** * BitMatrix转BufferedImage - * - * @param matrix BitMatrix + * + * @param matrix BitMatrix * @param foreColor 前景色 * @param backColor 背景色(null表示透明背景) * @return BufferedImage @@ -402,9 +402,9 @@ public class QrCodeUtil { BufferedImage image = new BufferedImage(width, height, null == backColor ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { - if(matrix.get(x, y)) { + if (matrix.get(x, y)) { image.setRGB(x, y, foreColor); - } else if(null != backColor){ + } else if (null != backColor) { image.setRGB(x, y, backColor); } } diff --git a/hutool-extra/src/test/java/cn/hutool/extra/qrcode/QrCodeUtilTest.java b/hutool-extra/src/test/java/cn/hutool/extra/qrcode/QrCodeUtilTest.java index 0552d6c61..f8600f8ff 100644 --- a/hutool-extra/src/test/java/cn/hutool/extra/qrcode/QrCodeUtilTest.java +++ b/hutool-extra/src/test/java/cn/hutool/extra/qrcode/QrCodeUtilTest.java @@ -1,16 +1,14 @@ package cn.hutool.extra.qrcode; -import java.awt.Color; -import java.io.File; - import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.Console; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import org.junit.Ignore; import org.junit.Test; -import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; - -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.lang.Console; +import java.awt.Color; +import java.io.File; /** * 二维码工具类单元测试 @@ -61,7 +59,7 @@ public class QrCodeUtilTest { System.out.println(base64); byte[] bytes = FileUtil.readBytes( - new File("e:/pic/qr.png")); + new File("d:/test/qr.png")); String encode = Base64.encode(bytes); String base641 = QrCodeUtil.generateAsBase64("http://hutool.cn/", new QrConfig(400, 400), "png", encode); System.out.println(base641);