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;
  });
}
相关推荐
用泥种荷花14 分钟前
【LangChain.js学习】 文档加载(Loader)与文本分割全解析
前端
cxxcode1 小时前
Vite 热更新(HMR)原理详解
前端
HelloReader1 小时前
Tauri 架构从“WebView + Rust”到完整工具链与生态
前端
UIUV1 小时前
node:child_process spawn 模块学习笔记
javascript·后端·node.js
Bigger1 小时前
告别版本焦虑:如何为 Hugo 项目定制专属构建环境
前端·架构·go
烛阴2 小时前
Three.js 零基础入门:手把手打造交互式 3D 几何体展示系统
javascript·webgl·three.js
颜酱2 小时前
单调栈:从模板到实战
javascript·后端·算法
代码匠心3 小时前
AI 自动编程:一句话设计高颜值博客
前端·ai·ai编程·claude
_AaronWong4 小时前
Electron 实现仿豆包划词取词功能:从 AI 生成到落地踩坑记
前端·javascript·vue.js
cxxcode4 小时前
I/O 多路复用:从浏览器到 Linux 内核
前端