小场景 小问题 小bug 小意思 小知识点
场景描述
- 接口层面:只接受File对象文件资源
- 接口要求之一:返回的File对象中必须存在图片的格式
- 实际前端拿到的base图片格式如下:
/9j/4AAQSkZJRgABAQAAAQABAAD/4gIYSUNDX1BST0ZJTEUAAQEAAAIIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAE
AAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQA···
问题
细心的同学会发现 这个base图片编码为什么没有图片前缀 没有 data:image/;base64, 前缀
正常情况下 一个正常的base图片编码如下
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAhgAAAN1CAIAAACy+xV3AAAAAXNSR0IArs4c6QAAAARnQ
U1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAP+lSURBVHhe7P15e···
没有图片前缀的话 解析出来的file对象图片信息也会缺失 图片是无法正常展示的 后端也无法正常的来解析file对象
四步解决方案
- 检测图片格式
常见的图片格式(如PNG、JPEG、GIF、WebP等),它们都有特定的文件头标识符。 这些标识符在Base64编码后的字符串开头部分依然保留了
例如:
> PNG格式的文件头通常是137 80 78 71 13 10 26 10
,对应的Base64编码前缀是iVBOR
。
> JPEG格式的文件头通常是255 216 255
,对应的Base64编码前缀是/9j/
。
> GIF格式的文件头通常是71 49 46 38
,对应的Base64编码前缀是R0lG
。
> WebP格式的文件头通常是82 73 70 56 80 38
,对应的Base64编码前缀是`UEsD
(详见detectImageFormat函数(再详见 百度))
- 生成file对象所需的imgName
- 将Base64字符串转换为Blob对象 再将Blob转换为File对象
- 收官 此时已经拿到了所有的图片信息
图片的格式(ImageFormat)在转换file对象过程中十分重要 做了这么多操作就是为了可以正确的获取到图片的格式
/**
* 解析base图片信息并转换为file对象
* @param {string} base64String 图片的Base64字符串
*/
analyzeImageInformation(base64String) {
// 检测图片格式
const format = this.detectImageFormat(base64String)
// 生成图片名称
const imgName = `${guid()}.${format}`
// 将Base64字符串转换为File对象
const file = this.base64ToFile(base64String, imgName, format)
// 创建图片信息对象
const imgInfo = {
imgUrl: this.addBase64Prefix(base64String, format),
file: file,
imgName: imgName,
}
// imgInfo 最终图片信息
},
/**
* 将Base64字符串转换为File对象
* @param {string} base64 Base64字符串
* @param {string} fileName 文件名
* @param {string} format 图片格式
* @returns {File} 转换后的File对象
*/
base64ToFile(base64, fileName, format) {
// 添加Base64前缀
const prefixedBase64 = this.addBase64Prefix(base64, format)
// 匹配Base64字符串的格式
const match = prefixedBase64.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/)
if (!match) {
throw new Error('Invalid input Base64 string')
}
// 获取Base64内容
const b64Content = match[2]
// 将Base64内容转换为字节字符
const byteCharacters = atob(b64Content)
// 创建字节数组
const byteNumbers = new Array(byteCharacters.length)
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i)
}
// 创建Uint8Array数组
const byteArray = new Uint8Array(byteNumbers)
// 创建Blob对象
const blob = new Blob([byteArray], { type: match[1] })
// 将Blob转换为File对象
return new File([blob], fileName, { type: match[1] })
},
/**
* 检测图片格式
*/
detectImageFormat(base64) {
// 读取Base64字符串的一部分作为图片头信息
const header = base64.substring(0, 10)
// 根据不同的图片格式前缀判断图片格式
if (header.startsWith('iVBOR')) {
return 'png'
} else if (header.startsWith('/9j/')) {
return 'jpeg'
} else if (header.startsWith('R0lG')) {
return 'gif'
} else if (header.startsWith('UEsDB')) {
return 'webp'
} else {
throw new Error('Unsupported image format')
}
},
/**
* 为Base64字符串添加前缀
*/
addBase64Prefix(base64, format) {
return `data:image/${format};base64,${base64}`
},