前言

最近真的被图片上传的功能给烦恼了。在web的项目中,我们经常会有上传图片的业务场景,最典型的是上传头像。为了解决头像上可以有如下的实现:

使用 multipart/form-data 上传用户信息和头像,也即是使用html里面的

。如 gitlab中修改用户信息的头像。

先将图片上传到图片服务,并获取图片连接,之后再用这个图片连接修改用户信息。

直接上传图片的Base64编码信息,作为图片的数据,后台再将编码转化为图片文件。

这里将讨论的是第三中实现方法的中的图片与Base64编码互转。

在网页中,会有如下两种处理图片的方式,一种是直接src="/avatar/avatar.jpg",另一种则是 src="https://img-blog.csdnimg.cn/2022010703580471583.jpeg"的方式。第二种方式就是前端将发给后台的内容,数据由[数据描述],[数据Base64]组成,[数据描述]将告知我们该图片的类别,可以从中分析出图片的拓展名,[数据Base64]为图片Base64编码之后的数据,为图片文件完整的数据。为了能够完整地回复图片的内容和拓展名,需要前端发送[数据描述],[数据Base64]到后台。该格式其实是Data URI Scheme,完整后面再做讲解。

Insert title here

Data URLs Image:

avatar.jpg

从 [数据描述] 判断图片拓展名

具体实现

数据描述与拓展名的映射

这里利用两个map,分别记录数据描述映射到拓展名和拓展名映射到数据描述,从而方便数据描述和拓展名的获取。

import java.util.HashMap;

import java.util.Map;

import java.io.File;

public class ImageDataURISchemeMapper {

private static Map scheme2Extension = new HashMap();

private static Map extension2Scheme = new HashMap();

static {

initSchemeSupported();

}

public static String getScheme(String imageExtension) {

if (imageExtension == null || imageExtension.isEmpty()) {

return "";

}

String result = extension2Scheme.get(imageExtension.toLowerCase());

return result == null ? "" : result;

}

public static String getScheme(File image) {

if (image == null) {

return "";

}

String name = image.getName();

int lastPointIndex = name.lastIndexOf(".");

return lastPointIndex < 0 ? "" : getScheme(name.substring(lastPointIndex + 1));

}

public static String getExtension(String dataUrlScheme) {

return scheme2Extension.get(dataUrlScheme);

}

public static String getExtensionFromImageBase64(String imageBase64, String defaultExtension) {

int firstComma = imageBase64.indexOf(",");

if(firstComma < 0) {

return defaultExtension;

}

return scheme2Extension.get(imageBase64.subSequence(0, firstComma + 1));

}

private static void initSchemeSupported() {

addScheme("jpg", "data:image/jpg;base64,");

addScheme("jpeg", "data:image/jpeg;base64,");

addScheme("png", "data:image/png;base64,");

addScheme("gif", "data:image/gif;base64,");

addScheme("icon", "data:image/x-icon;base64,");

}

private static void addScheme(String extension, String dataUrl) {

scheme2Extension.put(dataUrl, extension);

extension2Scheme.put(extension, dataUrl);

}

}

图片转Base64及Base64转图片

图片转Base64:

将图片文件读取为数据流,并转化为byte数组

将byte数组进行Base64编码,并转化为字符串

根据文件的拓展名添加数据描述前缀

Base64转图片:

将Base64字符串分成数据描述和数据Base64两个部分

通过数据描述部分获得图片拓展名

将数据Base64进行Base64解码,得到byte数组

保存byte数组到文件,如果保存的文件路径提供完整的文件名称,则无需所得拓展名,否则使用所得拓展名作为图片文件拓展名

import java.io.BufferedOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.nio.charset.StandardCharsets;

import java.util.Base64;

import java.util.HashMap;

import java.util.Map;

import org.apache.commons.io.IOUtils;

/**

* 目标处理的图片类别有:png,jpg,jpeg

*

* 参考:

*

*

[浅析data:image/png;base64的应用](https://www.cnblogs.com/ECJTUACM-873284962/p/9245474.html)

*

[Base64](https://zh.wikipedia.org/wiki/Base64)

*

[Data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs)

*

*

* @author DoneSpeak

* @date 2019/06/26

*/

public class ImageConvertBase64 {

/**

* 将图片文件转化为 byte 数组

*

* @param image

* 待处理图片文件

* @return 图片文件转化为的byte数组

*/

public static byte[] toBytes(File image) {

try (FileInputStream input = new FileInputStream(image)) {

// InputStream 的 available() 返回的值是该InputStream 在不被阻塞的情况下,一次可以读取到的数据长度。

// byte[] imageBytes = new byte[input.available()];

// input.read(imageBytes);

return IOUtils.toByteArray(input);

} catch (IOException e) {

return null;

}

}

public static String toBase64(byte[] bytes) {

return bytesEncode2Base64(bytes);

}

/**

* 将图片转化为 base64 的字符串

*

* @param image

* 待处理图片文件

* @return 图片文件转化出来的 base64 字符串

*/

public static String toBase64(File image) {

return toBase64(image, false);

}

/**

* 将图片转化为 base64 的字符串。如果appendDataURLScheme的值为true,则为图片的base64字符串拓展Data URL scheme。

* @param image 图片文件的路径

* @param appendDataURLScheme 是否拓展 Data URL scheme 前缀

* @return 图片文件转化为的base64字符串

*/

public static String toBase64(File image, boolean appendDataURLScheme) {

String imageBase64 = bytesEncode2Base64(toBytes(image));

if(appendDataURLScheme) {

imageBase64 = ImageDataURISchemeMapper.getScheme(image) + imageBase64;

}

return imageBase64;

}

private static String bytesEncode2Base64(byte[] bytes) {

return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8);

}

private static byte[] base64Decode2Bytes(String base64) {

return Base64.getDecoder().decode(base64);

}

/**

* 将byte数组恢复为图片文件

*

* @param imageBytes

* 图片文件的 byte 数组

* @param imagePath

* 恢复的图片文件的保存地址

* @return 如果生成成功,则返回生成的文件路径,此时结果为参数的imagePath。否则返回 null

*/

public static File toImage(byte[] imageBytes, File imagePath) {

if (!imagePath.getParentFile().exists()) {

imagePath.getParentFile().mkdirs();

}

try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(imagePath))) {

bos.write(imageBytes);

return imagePath;

} catch (IOException e) {

return null;

}

}

/**

* 将base64字符串恢复为图片文件

*

* @param imageBase64

* 图片文件的base64字符串

* @param imagePath

* 恢复的图片文件的保存地址

* @return 如果生成成功,则返回生成的文件路径,此时结果为参数的imagePath。。否则返回 null

*/

public static File toImage(String imageBase64, File imagePath) {

// base64 字符串中没有 ","

int firstComma = imageBase64.indexOf(",");

if(firstComma >= 0) {

imageBase64 = imageBase64.substring(firstComma + 1);

}

return toImage(base64Decode2Bytes(imageBase64), imagePath);

}

/**

* 保存 imageBase64 到指定文件中。如果fileName含有拓展名,则直接使用fileName的拓展名。

* 否则,如果 imageBase64 为Data URLs,则更具前缀的来判断拓展名。如果无法判断拓展名,则使用“png”作为默认拓展名。

* @param imageBase64 图片的base64编码字符串

* @param dir 保存图片的目录

* @param fileName 图片的名称

* @return 如果生成成功,则返回生成的文件路径。否则返回 null

*/

public static File toImage(String imageBase64, File dir, String fileName) {

File imagePath = null;

if(fileName.indexOf(".") < 0) {

String extension = ImageDataURISchemeMapper.getExtensionFromImageBase64(imageBase64, "png");

imagePath = new File(dir, fileName + "." + extension);

} else {

imagePath = new File(dir, fileName);

}

return toImage(imageBase64, imagePath);

}

}

利用第三方工具类简化代码。

这里使用的工具类为 org.apache.commons.io.*:

commons-io

commons-io

2.6

获取文件拓展名

int lastPointIndex = filename.lastIndexOf(".");

String extension = lastPointIndex < 0 ? "" : getScheme(filename.substring(lastPointIndex + 1));

// 简化

String extension = FilenameUtils.getExtension(filename);

文件转 byte[]

File image = new File("avatar.jpg");

IOUtils.toByteArray(new FileInputStream(image));

// 或者

byte[] bytes = FileUtils.readFileToByteArray(image);

byte[] 保存为文件

File image = new File("avatar.jpg")

FileUtils.writeByteArrayToFile(image, bytes);

实现图片转Base64和Base64转图片的代码其实就只要如下的几行代码。

public static void main(String[] args) throws IOException {

File image = new File("src/test/resources/imageConvertBase64.jpeg");

File newImage = new File("src/test/resources/new-imageConvertBase64.jpeg");

// 编码为 Base64 字符串

byte[] bytes = FileUtils.readFileToByteArray(image);

String base64 = new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8);

System.out.println(base64);

// Base64 保存为图片

bytes = Base64.getDecoder().decode(base64);

FileUtils.writeByteArrayToFile(newImage, bytes);

}

拓展知识

Data URLs

Data URLs, URLs prefixed with the data: scheme, allow content creators to embed small files inline in documents.

Data URLs 由如下四个部分组成,如文章开篇的 data:image/jpeg;base64,/9j/4AAQSkxxxxxxx。

data:[][;base64],

data: : 协议固定前缀。

[] : 是一个 MIME type,比如 image/jpeg,你也可以在 "Incomplete list of MIME types"中找到一些类型。

[;base64] : 是编码方式。 这里用的 base64。

: 编码后的字符串。

Data URL schema 是在 RFC2397 中被定义的URL scheme。其有如下的优缺点:

优点

减少请求

当外部资源受限是可以使用

缺点

无法重复使用

无法独立缓存 (可以利用css的background-image和css文件一起缓存)

base64会比原始文件增加 1/3 的大小

目前Data URL schema支持的类型有:

类型

描述

data:,

文本数据

data:text/plain,

文本数据

data:text/html,

HTML代码

data:text/html;base64,

base64编码的HTML代码

data:text/css,

CSS代码

data:text/css;base64,

base64编码的CSS代码

data:text/javascript,

Javascript代码

data:text/javascript;base64,

base64编码的Javascript代码

data:image/gif;base64,

base64编码的gif图片数据

data:image/png;base64,

base64编码的png图片数据

data:image/jpeg;base64,

base64编码的jpeg图片数据

data:image/x-icon;base64,

base64编码的icon图片数据

Base64

Base64是一种基于64个可打印字符来表示二进制数据的表示方法。任何数据都是二进制数据,也就是说Base64可以表示任何数据。Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据,包括MIME的电子邮件及XML的一些复杂数据。其主要的主要作用不在于安全,而在于让数据在网络中无错传输。

由于 2^6=64,所以每6个bit为一个单元,对应某个可打印字符。3个字节(byte)有24个bit,对应于4个Base64单元(24/6=4),即3个字节可由4个可打印字符来表示。这就导致编码后的数据比原始数据增加了1/3的长度。每6个bit的取值按照 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 进行选择。如原数据长度不是3的倍数,则剩下一个输入字符,添加一个=,两个输入字符,编码结果则增加一个=。

编码「Man」

在最后的一位(A)或者两位字符(BC)时进行编码

解码

编码的逆过程,将 = 转化为 0 即可,在 ASCII 码中,0字符为空字符。

以上的内容基本来自 维基百科 Base64。由于+/的特殊性,为了适应不同的场景,会使用不同的字符替换掉原来算法中的 +/,而形成新的算法。

在Java 8中,JDK提供了Base64的编解码工具。提供了 Basic编码 URL编码 MIME编码 以及 对流的封装,文章 Java 8实现BASE64编解码 中对 Base64 工具做了不错的介绍。

Base64 转化工具:

java converttobase64_Java 工具箱 | 图片-Base64 互转相关推荐

  1. Java图片Base64互转

    这个工具依赖 commons-codec <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec --> & ...

  2. Java IO流InputString 与 Base64互转

    InputString Base64 互转 IO Base64 互转 产生问题 - BASE64Encoder报错 原因 解决方案 IO Base64 互转 话不多说直接上代码 public Stri ...

  3. redis java 存储图片_Redis 存储图片 [base64/url/path]vs[object]

    一.base64图片编解码 基本流程:从网络获取下载一张图片.然后base64编码,再base64解码,存到本地E盘根文件夹下. import java.awt.image.BufferedImage ...

  4. 2020-12-11 图片格式互转:base64、PIL Image opencv cv2互转

    图片格式互转:base64.PIL Image opencv cv2互转 base64 to PIL Image import base64 from io import BytesIO from P ...

  5. java实现将图片读取成base64字符串,将base64字符串存储为图片。

    全栈工程师开发手册 (作者:栾鹏) java教程全解 java实现将图片读取成base64字符串 ,将base64字符串存储为图片. 将图片转化为字符串以后,由于字符串更方便在网络上通过ajax传输. ...

  6. java 微信开发图片发送,微信开发?Java上传Base64图片

    class="java">import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Log ...

  7. Java教程:Java使用POI将图片Base64编码写入到Excel表格当中

    今天来说下在Java当中使用poi将将图片Base64编码写入到Excel表格当中,以前我们都是在表格中写内容,但不防会有时让写出图片等功能,比如说做一些评价功能,上传图片那是必然的,接下来我就说下整 ...

  8. Java接受前端的base64,转换失败。base64转图片互相转换

    spring boot前后端vue,base64转图片互相转换 vue就不展示了,下边只展示Java中互相转换的例子. Java实现图片转化成base64字符串 //图片转化成base64字符串 pu ...

  9. java -- cropper裁剪图片并base64上传 移动端简单示例

    前言 cropper是一款使用简单且功能强大的图片剪裁jQuery插件.该图片剪裁插件支持图片放大缩小,支持图片旋转,支持触摸屏设备,支持canvas,并且支持跨浏览器使用. cropper有两种方式 ...

最新文章

  1. Linux Shell简介
  2. IT英语职场之网管英语大全
  3. PaperNotes(15)-图神经网络、PyG极简版入门笔记
  4. javascript高级程序设计-Array迭代及归并
  5. 大牛讲解Kubernetes实战
  6. Retrofit+Rxjava服务器IP轮询重试机制实现
  7. 【AAA】AAA协议介绍
  8. 树莓派3B 开启串口
  9. ZigBee 集中式网络与分布式网络
  10. java调用cmd命令
  11. VM虚拟机安装Linux系统
  12. JAVA程序员就业面试题大全
  13. 思念绵绵,爱在彼此心间漫延
  14. BIMC电子商务外包服务独特之处
  15. 计算机屏幕尺寸不是全屏,电脑屏幕有黑边撑不满怎么办_电脑屏幕不能全屏显示的解决方法...
  16. 《把时间当作朋友》读后感
  17. 圣诞html源代码,html5 3D圣诞树源码
  18. STM32CubeIDE开发(二十二), stm32的RS485/232串口通信开发要点
  19. 【畅通工程 HDU - 1232 】【并查集模板题】
  20. Caffe2 - (七)Caffemodel 转换为 Caffe2 pb 模型

热门文章

  1. Blazor 服务器上带有 EF Core 的 Azure Cosmos DB
  2. .net core精彩实例分享 -- 应用配置和数据库访问
  3. 使用UWP人脸检测API在WPF中进行人脸检测
  4. PostgreSQL 12:新功能和增强功能
  5. ASP.NET Core 2.0身份和角色管理入门
  6. pca算法python实现_三种方法实现PCA算法(Python)
  7. oracle取第一位,Oracle中的substr()函数和INSTR()函数
  8. rust里面的awm叫什么_铅笔里面有铅吗?为什么叫铅笔呢?
  9. 启动模拟器要20多分钟_有硬核开发者将Windows 10X模拟器镜像提取并在物理机上成功安装...
  10. element table表格里的多选按钮,根据条件判断是否可以被选中