场景描述:
项目开发初期,对于一些资源上传功能和产品经理沟通过,图片类是png、jpg两个格式,所以上传组件开发也是只限制了这两个格式文件,那么到了生产环境客户给的格式是svg,其实到这里给前端也没多大影响加个type就行,那么问题就来了,在预览图片的时候图片无法加载显示???
问题分析:图片 -> 接口 -> redis服务 -> 文件链接url -> 浏览器预览
png/jpg文件这一套流程都没问题,那么问题就是出在svg上
对比了解得知:
PNG - 纯二进制格式
- 纯粹的图像数据
- 浏览器只能将其作为图片处理
- 没有其他用途,所以默认就是预览
SVG - 文本格式的 XML
arduino
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" fill="red" />
</svg>
- 既是图片,也是可读的文本文件
- 可能包含脚本和样式,有安全考虑
- 服务器可能出于安全原因强制下载
浏览器在预览的处理策略
PNG 的处理:
xml
<!-- 浏览器看到 PNG 只会尝试渲染为图片 -->
<img src="image.png" alt="示例">
SVG 的处理:
浏览器需要决定:
- 是作为图片渲染?
- 是作为 XML 文档显示源代码?
- 还是因为有安全风险而强制下载?
并且对比接口响应头
后端服务返回的是 content-type: application/json
能够正确预览的情况下应该是 Content-Type: image/svg+xml
所以浏览器识别到不对的响应头会去触发强制下载,img标签预览地址限制所以无法加载预览
前端解决思路
既然svg预览不了,那我就把svg转成png
js
// UI上就正常可以预览了
<el-table-column label="图片" prop="resUrl" align="center" :show-overflow-tooltip="true" width="300">
<template slot-scope="scope">
<el-image
style="width: 100px; height: 100px"
:src="scope.row._previewUrl || scope.row.resUrl"
>
</el-image>
</template>
</el-table-column>
js
async svgUrlToPng(svgUrl, width = 100, height = 100) {
const res = await fetch(svgUrl);
const svgText = await res.text();
const svgBase64 = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svgText);
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = "anonymous";
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
resolve(canvas.toDataURL("image/png"));
};
img.onerror = reject;
img.src = svgBase64;
});
}
getListData() {
this.loading = true;
getList(this.queryParams).then(async response => {
const rows = response.rows;
// 处理每一行
for (let row of rows) {
if (row.resUrl && row.resUrl.endsWith(".svg")) {
try {
row._previewUrl = await this.svgUrlToPng(row.resUrl, 100, 100);
} catch (e) {
console.error("SVG 转换失败:", e);
row._previewUrl = row.resUrl;
}
}
}
this.roleList = rows;
this.total = response.total;
this.loading = false;
});
}
