解决图片上传 404 错误的技术之旅:从 [object%20Object] 到完美兼容!!!

解决图片上传 404 错误的技术之旅:从 [object%20Object] 到完美兼容 🚀

背景 🎬

最近在开发一个 Vue 项目时,遇到一个棘手的 404 错误 😓。用户在上传图片时,浏览器请求了一个奇怪的 URL:

http://域名/[object%20Object]

这个 [object%20Object] 让我瞬间懵圈 🤯。显然,代码在构造图片 URL 时,将一个 JavaScript 对象直接拼接进了字符串,导致了 404 错误。我们的目标是修复这个问题,同时确保新代码兼容之前的字符串格式,不能影响已有功能 💪。

这篇博客将记录整个排查和解决过程,分享经验教训,并用图表和流程图让你更直观地理解问题和解决方案 🖼️。


问题分析 🔍

1. 错误现象

用户在 identify-form.vue 组件中上传图片时,浏览器发出了一个错误的请求:

  • 请求 URLhttp://域名/[object%20Object]
  • 状态码:404 Not Found
  • Refererhttp://localhost:9527/(本地开发环境)
  • 代理127.0.0.1:7890(本地代理,未影响问题)

显然,[object%20Object] 是 JavaScript 对象被错误转换为字符串的结果。问题出在图片 URL 的构造上。

2. 代码上下文

问题发生在 identify-form.vue 中的 <w-form-multiple-image> 组件,用于处理图片上传:

<w-form-multiple-image
  v-model="form.images"
  label="图片附件"
  label-width="120px"
  :operate-type="operateType"
  folder-name="fake-strategy"
  :limit="5"
/>
  • form.images:存储上传后的图片路径,可能是字符串数组(["fake-strategy/...", ...])或对象数组([{ original: "...", ... }, ...])。
  • folder-name="fake-strategy":图片上传到 fake-strategy 文件夹。

<w-form-multiple-image> 组件内部使用 UploadImage 组件(src/components/UploadImage/multiple.vue)来执行实际的上传操作。

3. 上传 API 返回的数据

UploadImage 组件调用上传 API,返回的数据结构如下:

{
    "code": 0,
    "msg": "上传成功",
    "data": {
        "original": "fake-strategy/fd653f81-c4ff-4daa-aaac-0e140aecbe71_Generated Image April 19, 2025 - 8_14PM.jpeg",
        "thumb": "fake-strategy/thumb_fd653f81-c4ff-4daa-aaac-0e140aecbe71_Generated Image April 19, 2025 - 8_14PM.jpeg",
        "medium": "fake-strategy/medium_fd653f81-c4ff-4daa-aaac-0e140aecbe71_Generated Image April 19, 2025 - 8_14PM.jpeg"
    }
}
  • data.original 是原始图片路径,thumbmedium 是缩略图和中等尺寸图片的路径。
  • UploadImage 组件将整个 data 对象({ original: "...", thumb: "...", medium: "..." })返回,导致问题。

4. 问题根因

w-form-multiple-image.vue 中,view 模式下渲染图片时,直接拼接 oss + item

<el-image
  v-for="item, index in val"
  :src="oss + item"
  :preview-src-list=[${oss}${item}]
/>
  • 问题valUploadImage 返回的对象数组([{ original: "...", ... }, ...]),oss + item 会将对象转换为 [object%20Object],导致错误的 URL。
  • 之前的功能val 可能是字符串数组(["fake-strategy/...", ...])或逗号分隔字符串("fake-strategy/...,..."),需要兼容。

解决过程 🛠️

1. 排查问题

我们从以下几个方面分析问题:

  • URL 构造[object%20Object] 表明代码将对象直接拼接进了 URL。
  • 组件逻辑w-form-multiple-image.vue 使用 UploadImage 组件,UploadImage 返回对象数组。
  • 数据格式val 需要支持字符串、字符串数组和对象数组。

2. 解决方案

我们修改了 w-form-multiple-image.vue,主要目标是:

  1. 支持对象格式:从 UploadImage 返回的对象中提取 original 字段。
  2. 兼容字符串格式:支持之前的字符串数组和逗号分隔字符串。
  3. 修复 404 错误:确保图片 URL 构造正确。
关键改动
  • 添加 formattedImages 计算属性:将 val 转换为正确的图片 URL 数组。
  • 添加 imageListForUpload 计算属性:确保 UploadImage 接收到字符串数组。
  • 调整 handleUploadSucc:根据 val 的类型(字符串或数组)决定更新方式。
修改后的代码片段

以下是 w-form-multiple-image.vue 的关键部分:

<template>
  <div v-if="val.length && operateType === 'view'">
    <el-image
      v-for="item, index in formattedImages"
      :key="index + 'el-image'"
      :src="item"
      :preview-src-list="[item]"
    />
  </div>
  <upload-image
    v-else
    :image-list="imageListForUpload"
    @upload-success="handleUploadSucc"
    @handle-remove="handleUploadSucc"
  />
</template>

<script lang="ts">
export default class extends Vue {
  public val: any = '';

  get formattedImages(): string[] {
    if (typeof this.val === 'string') {
      if (this.val === '') return [];
      return this.val.split(',').map(item => `${this.oss}${item.trim()}`);
    }
    if (Array.isArray(this.val)) {
      return this.val.map(item => {
        if (typeof item === 'string') return `${this.oss}${item}`;
        if (item && typeof item === 'object' && item.original) return `${this.oss}${item.original}`;
        return '';
      }).filter(url => url);
    }
    return [];
  }

  get imageListForUpload(): any[] {
    if (typeof this.val === 'string') {
      if (this.val === '') return [];
      return this.val.split(',').map(item => item.trim());
    }
    if (Array.isArray(this.val)) {
      return this.val.map(item => typeof item === 'string' ? item : item.original).filter(item => item);
    }
    return [];
  }

  public handleUploadSucc(images: any) {
    const formattedImages = Array.isArray(images)
      ? images.map(item => typeof item === 'string' ? item : item.original).filter(item => item)
      : [];
    this.val = typeof this.val === 'string' ? formattedImages.join(',') : formattedImages;
    this.$emit('input', this.val);
  }
}
</script>

关键点总结 📊

以下表格总结了问题的关键点和解决方案:

方面问题解决方案
URL 构造oss + item 导致 [object%20Object]使用 formattedImages 计算属性,正确拼接 oss 和图片路径。
数据格式val 可能是字符串、数组或对象数组formattedImageshandleUploadSucc 中支持所有格式。
UploadImage返回对象数组([{ original: "...", ... }]handleUploadSucc 中提取 original 字段,转换为字符串数组。
兼容性之前的功能依赖字符串或字符串数组根据 val 类型动态更新(字符串用逗号分隔,数组保持数组格式)。
初始化val 未及时同步父组件的 value@Watch('value') 中添加 immediate: true

流程图:图片上传和展示流程 📈

以下是用 Mermaid 绘制的流程图,展示图片上传和展示的流程:

对象数组
字符串数组
根据 val 类型
根据 val 类型
用户上传图片
UploadImage 组件触发 upload-success
handleUploadSucc 处理上传结果
提取 original 字段
保持不变
转换为字符串数组
val 是字符串: 转换为逗号分隔字符串
val 是数组: 保持数组格式
更新 val 和 form.images
view 模式: 使用 formattedImages 渲染图片
生成正确 URL: oss + 图片路径
图片正常显示

序列图:组件交互过程 🕒

以下是用 Mermaid 绘制的序列图,展示组件之间的交互:

用户 identify-form.vue w-form-multiple-image.vue UploadImage 组件 服务器 上传图片 传递 form.images (v-model) 调用上传 (:image-list) 发起上传请求 返回 { original: ..., thumb: ..., medium: ... } 触发 upload-success 事件 handleUploadSucc: 提取 original 更新 form.images (emit input) view 模式: 渲染图片 formattedImages: 生成正确 URL 显示图片 用户 identify-form.vue w-form-multiple-image.vue UploadImage 组件 服务器

经验教训 🌟

  1. 类型安全很重要 🔒
    如果 val 的类型定义更严格(例如 string[] 而不是 string),问题可能更早暴露,避免运行时错误。

  2. 日志是排查利器 📜
    handleUploadSuccwatchVal 中添加 console.log,帮助我们快速定位 valimages 的格式。

  3. 兼容性优先 ⚖️
    修改代码时,必须考虑已有功能。我们通过动态判断 val 的类型,确保新老格式都能支持。

  4. 流程图和序列图 📊
    使用 Mermaid 绘制流程图和序列图,直观展示问题和解决方案,提升文档可读性。


思维导图:问题解决全景 🧠

以下是 Markdown 格式的思维导图,总结整个过程:

在这里插入图片描述


总结 🎉

通过这次排查,我们成功解决了图片上传的 404 错误,从 [object%20Object] 的迷雾中走了出来 🌞。不仅修复了问题,还让代码更健壮,支持了新老数据格式。希望这篇博客能帮到遇到类似问题的开发者,也欢迎大家分享自己的经验!💬

如果你有其他问题,欢迎在评论区留言,我们一起探讨!👇

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值