http接口响应头类型不对,导致svg图片无法预览,前端解决方案

场景描述:

项目开发初期,对于一些资源上传功能和产品经理沟通过,图片类是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;
  });
}
相关推荐
骑着蜗牛撵大象3273 分钟前
LangChain 工具调用从声明到闭环:给智能体接上真实的手脚
前端
数据掘金13 分钟前
百度统计后台页面数据异常?从手动调用时机开始排查
前端
hunteritself14 分钟前
10 层 4K 实时剪辑,国产工作站进入广电制作核心
大数据·前端·人工智能·深度学习·transformer
踩着两条虫15 分钟前
我在浏览器里“盖”了一个村:Vue + Three.js 大屏开发踩坑记
前端·vue.js·three.js
默_笙19 分钟前
🧀 让大模型的答案"流"出来:NestJS AI 模块 + SSE + RxJS 实战
javascript·人工智能
mbshqqb20 分钟前
TypeScript基础教程之同步生成器
前端·javascript·typescript
嘟嘟同学和妮妮同学39 分钟前
【升级】一个中国历史可视化站:皇帝时间线梳理、古代思想流派、诗词鉴赏 (2)
前端·html
暗夜行者之光1 小时前
LangGraph + MCP 实战:构建"自然语言查数据"的 ChatBI Agent
前端·后端
一tiao咸鱼1 小时前
还在手动海投?我做了个 AI 求职助手,自动筛选 BOSS 职位再精准投递
前端·后端·面试
尤水就下1 小时前
🔥预热:在线文档的 block 架构是如何实现的(未完待续...)
前端·javascript·架构