Angular图片转WebP后下载显示灰色方块问题排查
Angular图片转WebP后仅显示灰色方块的问题排查与优化方案
问题描述
我尝试在Angular服务中将图片转换为WebP格式,流程看似正常,但调用测试函数触发下载后,得到的图片仅是一个灰色方块。请问哪里操作有误?是否有更简便的实现方式?
原服务代码
image-conversion.service.ts
import { Injectable } from '@angular/core'; import { DOC_ORIENTATION, NgxImageCompressService } from 'ngx-image-compress'; @Injectable({ providedIn: 'root' }) export class ImageConversionService { constructor(private imageCompress: NgxImageCompressService) { } convertToWebP(image: File): void { const reader = new FileReader(); reader.readAsDataURL(image); reader.onload = () => { const dataUrl = reader.result as string; this.imageCompress.compressFile(dataUrl, DOC_ORIENTATION.Up, 50, 50).then(result => { const convertedImage = new File([result], `${image.name.split('.')[0]}.webp`, { type: 'image/webp' }); console.log('converted and file created!'); console.log(convertedImage); this.downloadFile(convertedImage, "test") }); }; } // test function to trigger download of converted image private downloadFile(data: Blob, filename: string): void { const url = URL.createObjectURL(data); const link = document.createElement('a'); link.href = url; link.download = filename; link.style.display = 'none'; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } }
现象说明
- 日志输出:显示
converted and file created!,且打印出的File对象有正确的文件名和大小 - 下载结果:得到的是一个仅显示灰色方块的无效WebP图片
错误原因分析
核心问题在于直接将base64字符串作为File构造函数的数据源。ngx-image-compress返回的result是base64格式的DataURL(格式如data:image/jpeg;base64,xxxx),直接传入File后,文件内容会是纯文本格式的base64编码,而非真正的二进制图片数据,导致浏览器无法正确解析,最终显示为灰色方块。
修正后的代码
需要先将base64的DataURL转换为Blob二进制数据,再创建File对象:
import { Injectable } from '@angular/core'; import { DOC_ORIENTATION, NgxImageCompressService } from 'ngx-image-compress'; @Injectable({ providedIn: 'root' }) export class ImageConversionService { constructor(private imageCompress: NgxImageCompressService) { } convertToWebP(image: File): void { const reader = new FileReader(); reader.readAsDataURL(image); reader.onload = () => { const dataUrl = reader.result as string; this.imageCompress.compressFile(dataUrl, DOC_ORIENTATION.Up, 50, 50).then(result => { // 将base64 DataURL转换为Blob const blob = this.dataURLtoBlob(result); // 用Blob创建正确的WebP文件 const convertedImage = new File([blob], `${image.name.split('.')[0]}.webp`, { type: 'image/webp' }); console.log('converted and file created!'); console.log(convertedImage); this.downloadFile(convertedImage, `${image.name.split('.')[0]}.webp`) }); }; } // 新增:DataURL转Blob工具方法 private dataURLtoBlob(dataUrl: string): Blob { const arr = dataUrl.split(','); const mime = arr[0].match(/:(.*?);/)![1]; const bstr = atob(arr[1]); let n = bstr.length; const u8arr = new Uint8Array(n); while(n--){ u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], {type: mime}); } // test function to trigger download of converted image private downloadFile(data: Blob, filename: string): void { const url = URL.createObjectURL(data); const link = document.createElement('a'); link.href = url; link.download = filename; link.style.display = 'none'; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } }
更简便的实现方式
如果不需要压缩功能,完全可以不用依赖第三方库,直接用浏览器原生Canvas API完成图片转WebP:
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class ImageConversionService { convertToWebP(image: File): Promise<void> { return new Promise((resolve, reject) => { const img = new Image(); img.src = URL.createObjectURL(image); img.onload = () => { URL.revokeObjectURL(img.src); const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext('2d'); ctx?.drawImage(img, 0, 0); // 直接导出WebP格式的Blob canvas.toBlob((blob) => { if (!blob) { reject(new Error('Failed to convert image to WebP')); return; } const convertedImage = new File([blob], `${image.name.split('.')[0]}.webp`, { type: 'image/webp' }); this.downloadFile(convertedImage, `${image.name.split('.')[0]}.webp`); resolve(); }, 'image/webp', 0.5); // 最后一个参数是质量,取值范围0-1 }; img.onerror = reject; }); } private downloadFile(data: Blob, filename: string): void { const url = URL.createObjectURL(data); const link = document.createElement('a'); link.href = url; link.download = filename; link.style.display = 'none'; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } }
该方案无需引入第三方依赖,利用浏览器原生API完成转换,代码更简洁,也避免了第三方库带来的潜在问题。
内容的提问来源于stack exchange,提问作者andresmonc
相关产品推荐
相关产品推荐

