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;
  });
}
相关推荐
llxxyy卢1 分钟前
XSS跨站之订单及shell箱子反杀记
前端·xss
q***64975 分钟前
SpringSecurity踢出指定用户
android·前端·后端
q***76669 分钟前
SpringSecurity 实现token 认证
android·前端·后端
llxxyy卢9 分钟前
xss-maps(1-12)尝试思路过关加源码分析
前端·xss
Amy_yang11 分钟前
js 封装时间格式化,将单位有秒(s)的数据转换为'00:00:00'格式
javascript
interception13 分钟前
爬虫js逆向,jsdom补环境,抖音,a_bogus
javascript·爬虫·python
小璞13 分钟前
一、React Fiber 架构与任务调度详解
前端·react.js·前端框架
小璞13 分钟前
四、虚拟 DOM 与 Diff 算法:架构设计的智慧
前端·react.js·前端框架
南蓝14 分钟前
【AI 日记】调用大模型的时候如何按照 sse 格式输出
前端·人工智能
一树论17 分钟前
浏览器插件开发经验分享二:如何处理日期控件
前端·javascript