这几天在做一个视频推拉流的接口,具体就是在新增页面的时候,调用定时任务,拉流抽帧以及相关的表存储。重点是拉流抽帧。
在获取rtsp流地址的时候,调用抽帧接口,然后返回的是图片的base64编码,最后调用minio上传接口。
刚开始我是这样写的
String imgUrl = fileObjectService.uploadPicture(fileName, new ByteArrayInputStream(imgBase64.getBytes()));
在这里,new ByteArrayInputStream(imgBase64.getBytes())
直接将 Base64 字符串的字节数据作为输入流上传,而没有将 Base64 字符串解码为原始的二进制图片数据,这也导致了我下载到的图片是破损的。
解决方法
在将 Base64 字符串上传之前,需要先将其解码为二进制数据,然后再上传。以下是修改后的代码:
1. Base64 解码
-
使用
java.util.Base64
将 Base64 字符串解码为字节数组。
2. 上传解码后的二进制数据
-
将解码后的字节数组包装为
ByteArrayInputStream
,然后上传。
import java.util.Base64;
import java.io.ByteArrayInputStream;
// 假设 imgBase64 是 Base64 编码的图片数据
String imgBase64 = "..."; // 你的 Base64 图片数据
// 1. 解码 Base64 字符串为字节数组
byte[] imageBytes = Base64.getDecoder().decode(imgBase64);
// 2. 将字节数组包装为 InputStream
ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
// 3. 上传图片
String imgUrl = fileObjectService.uploadPicture(fileName, inputStream);
// 4. 记录日志
if (imgUrl != null) {
log.info("图片上传成功,图片地址: {}", imgUrl);
} else {
log.error("图片上传失败");
}
详细说明
-
Base64 解码:
-
使用
Base64.getDecoder().decode(imgBase64)
将 Base64 字符串解码为字节数组。 -
解码后的字节数组是图片的原始二进制数据。
-
-
创建输入流:
-
使用
ByteArrayInputStream
将字节数组包装为InputStream
。
-
-
上传图片:
-
将解码后的二进制数据上传到存储服务。
-
-
日志记录:
-
如果上传成功,记录图片地址;如果失败,记录错误信息。
-
【注】======================================================================
1、Base64编码的不能包括类似data:image/png;base64这种前缀的,要不然会报
java.lang.IllegalArgumentException: Illegal base64 character 3a
at java.util.Base64$Decoder.decode0(Base64.java:714)
at java.util.Base64$Decoder.decode(Base64.java:526)
at java.util.Base64$Decoder.decode(Base64.java:549)
at
从错误信息来看,java.lang.IllegalArgumentException: Illegal base64 character 3a
表明 Base64 字符串中包含非法字符(3a
是字符 :
的 ASCII 码)。Base64 字符串只能包含以下字符:
-
字母:
A-Z
,a-z
-
数字:
0-9
-
特殊字符:
+
,/
-
填充字符:
=
如果 Base64 字符串中包含其他字符(如 :
),会导致解码失败。
一般来说,这种Base64编码文件比较大,得有个5~6MB。所以建议测的时候可以自己另外建一个txt文件,然后读取文件内容即可。
// 读取base64.txt文件内容
File base64File = new File("D:\\xxx\\xxx\\base64.txt");
BufferedReader reader = new BufferedReader(new FileReader(base64File));
StringBuilder base64Content = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
base64Content.append(line);
}
reader.close();
// 将文件内容赋值给imgBase64变量
String imgBase64 = base64Content.toString();
log.info("设备{}的图片base64编码:{}", deviceId, imgBase64);
完整样例
import java.util.Base64;
import java.io.ByteArrayInputStream;
// 假设 imgBase64 是 Base64 编码的图片数据
// String imgBase64 = "..."; // 你的 Base64 图片数据
try {
// 1. 去除 Data URL 前缀(如果存在)
if (imgBase64.startsWith("data:image")) {
imgBase64 = imgBase64.split(",")[1];
}
// 2. 验证 Base64 字符串是否合法
if (!imgBase64.matches("^[A-Za-z0-9+/]+={0,2}$")) {
throw new IllegalArgumentException("非法的 Base64 字符串");
}
// 读取base64.txt文件内容
File base64File = new File("D:\\xxx\\xxx\\base64.txt");
BufferedReader reader = new BufferedReader(new FileReader(base64File));
StringBuilder base64Content = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
base64Content.append(line);
}
reader.close();
// 将文件内容赋值给imgBase64变量
String imgBase64 = base64Content.toString();
log.info("设备{}的图片base64编码:{}", deviceId, imgBase64);
// 3. 解码 Base64 字符串为字节数组
byte[] imageBytes = Base64.getDecoder().decode(imgBase64);
// 4. 将字节数组包装为 InputStream
ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
// 5. 上传图片
String imgUrl = fileObjectService.uploadPicture(fileName, inputStream);
// 6. 记录日志
if (imgUrl != null) {
log.info("图片上传成功,图片地址: {}", imgUrl);
} else {
log.error("图片上传失败");
}
} catch (IllegalArgumentException e) {
log.error("Base64 字符串非法: {}", imgBase64, e);
throw new RuntimeException("Base64 字符串非法,请检查数据格式");
} catch (Exception e) {
log.error("图片上传失败", e);
throw new RuntimeException("图片上传失败", e);
}
详细说明
-
去除 Data URL 前缀:
-
如果 Base64 字符串以
data:image/png;base64,
开头,使用split(",")
去除前缀,只保留 Base64 编码部分。
-
-
验证 Base64 字符串:
-
使用正则表达式
^[A-Za-z0-9+/]+={0,2}$
验证 Base64 字符串是否合法。 -
如果字符串中包含非法字符(如
:
),会抛出IllegalArgumentException
。
-
2、 如果在打日志断点的时候,idea的console控制台看不到整个日志(因为图片的base64很大)。
可以修改这个文件
保存配置,再重启Idea就行。