文件头标识符---Base64图片编码转File对象

405 阅读2分钟

小场景 小问题 小bug 小意思 小知识点

场景描述

  • 接口层面:只接受File对象文件资源
  • 接口要求之一:返回的File对象中必须存在图片的格式
  • 实际前端拿到的base图片格式如下:
/9j/4AAQSkZJRgABAQAAAQABAAD/4gIYSUNDX1BST0ZJTEUAAQEAAAIIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAE
AAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQA···

问题

细心的同学会发现 这个base图片编码为什么没有图片前缀 没有 data:image/;base64, 前缀

正常情况下 一个正常的base图片编码如下

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAhgAAAN1CAIAAACy+xV3AAAAAXNSR0IArs4c6QAAAARnQ
U1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAP+lSURBVHhe7P15e···

没有图片前缀的话 解析出来的file对象图片信息也会缺失 图片是无法正常展示的 后端也无法正常的来解析file对象

四步解决方案

  1. 检测图片格式
常见的图片格式(如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函数(再详见 百度))

  1. 生成file对象所需的imgName
  2. 将Base64字符串转换为Blob对象 再将Blob转换为File对象
  3. 收官 此时已经拿到了所有的图片信息

图片的格式(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}`
},