首页 新闻 会员 周边

后端响应图片输出流,vue无法显示

0
悬赏园豆:100 [待解决问题]

问题描述:后端响应文件输出流,前端采用vue技术,在img标签的src属性中定义一个函数,这个函数内部发送获取图片流,将图片流作为返回值,让img显示图片,但是出现了无法显示图片

具体代码如下:
请求

export function getIcon(iconInfo) {
    return request({
        method: 'post',
        url: apiUrl + 'getImage',
        data: iconInfo,
        responseType: 'blob'
    })
}

页面

<template>
  <div>
    <template v-for="item in menuList" :key="item.path">
      <!--      分为两种方式渲染:有子菜单和没有子菜单-->
      <el-sub-menu
          :index="item.path"
          v-if="item.nodeType == 1"
      >
        <template #title>
          <el-icon v-show="item.iconUrl!=null"><img :src="sendIcon(item.iconUrl)" alt="" class="icon_class"/></el-icon>
          <span>{{ item.name }}</span>
        </template>
        <!--        有子菜单的继续遍历(递归)-->
        <menuTree :menuList="item.children"></menuTree>
      </el-sub-menu>
      <!--      没有子菜单-->
      <el-menu-item :index="item.path" v-if="item.nodeType==2">
        <el-icon v-show="item.iconUrl!=null">
          <img :src="sendIcon(item.iconUrl)" alt="" class="icon_class"/>
        </el-icon>
        <span>{{ item.name }}</span>
      </el-menu-item>
    </template>
  </div>
</template>

<script>
import {getIcon} from '@/api/home/file/index'
import {onBeforeUnmount} from "vue";
export default ({
  name: 'menuTree',
  props: ['menuList'],
  setup(props) {
    function sendIcon(filePath) {
      // 获取菜单图片路径
      if (filePath != null) {
        let FileInfoVo = {
          //封装请求参数
          filePath: filePath
        }
        //获取图片输出流
        let a= getIcon(FileInfoVo).then(res => {
          // let blob= new Blob([res.data],{type: "image/png"});
          return URL.createObjectURL(res.data)
        })
        console.log(a)
        return a
      }
    }
 /*   onBeforeUnmount(()=>{
      if (this.blob) {
        URL.revokeObjectURL(this.blob);
      }
    })*/
    return {
      sendIcon
    };
  }
})
</script>


在上面中关键代码如下

<el-icon v-show="item.iconUrl!=null"><img :src="sendIcon(item.iconUrl)" alt="" class="icon_class"/></el-icon>
<el-icon v-show="item.iconUrl!=null">
          <img :src="sendIcon(item.iconUrl)" alt="" class="icon_class"/>
        </el-icon>

    function sendIcon(filePath) {
      // 获取菜单图片路径
      if (filePath != null) {
        let FileInfoVo = {
          //封装请求参数
          filePath: filePath
        }
        //获取图片输出流
        let a= getIcon(FileInfoVo).then(res => {
          // let blob= new Blob([res.data],{type: "image/png"});
          return URL.createObjectURL(res.data)
        })
        console.log(a)
        return a
      }
    }

其中输出结果:

Promise {<pending>}
[[Prototype]]
: 
Promise
[[PromiseState]]
: 
"fulfilled"
[[PromiseResult]]
: 
"blob:http://localhost:8080/f779df4f-4d93-4d60-a69c-a45ab21803bd"

不知道为什么就是不能显示,而且这个返回值也是一个promise对象

阿宝234的主页 阿宝234 | 初学一级 | 园豆:14
提问于:2023-08-02 07:48
< >
分享
所有回答(5)
1

你在sendIcon函数中使用了异步操作getIcon,并且返回了一个Promise对象。这可能导致在模板中无法正确显示图片。

你可以尝试使用Vue的computed属性来处理这个问题。computed属性可以根据data中的数据动态计算出一个新的值,并将其作为模板中的数据进行展示。

首先,你需要将sendIcon函数改写成一个异步函数,以便在其中使用await关键字等待getIcon方法的返回结果。然后,你可以将sendIcon函数放在Vue实例的computed属性中,并在模板中直接使用这个computed属性。

以下是修改后的代码示例:

<el-icon v-show="item.iconUrl!=null">
  <img :src="iconUrl" alt="" class="icon_class"/>
</el-icon>

data() {
  return {
    item: {
      iconUrl: null
    }
  }
},
computed: {
  async iconUrl() {
    if (this.item.iconUrl != null) {
      let FileInfoVo = {
        filePath: this.item.iconUrl
      }
      let res = await getIcon(FileInfoVo);
      return URL.createObjectURL(res.data);
    }
    return null;
  }
},
methods: {
  async getIcon(fileInfo) {
    // 发送获取图片流的请求
    // 返回图片流
  }
}

在这个示例中,我将item.iconUrl设置为null,你可以根据实际情况修改。computed属性iconUrl会根据item.iconUrl的值动态计算出一个新的值,并将其作为模板中的数据进行展示。getIcon方法用于发送获取图片流的请求,你需要根据实际情况进行实现。

通过这种方式,你应该能够正确地在模板中显示图片了。

lanedm | 园豆:2381 (老鸟四级) | 2023-08-02 08:38
0

问题在于 sendIcon 函数中,虽然你使用了 asyncawait,但是实际上 getIcon 函数返回的是一个Promise对象,并没有直接返回 res.data 的值。

你需要在 sendIcon 函数中使用 async/await 来等待 getIcon 函数的返回值,然后再将结果返回给 :src 属性。

<el-icon v-show="item.iconUrl != null">
  <img :src="await sendIcon(item.iconUrl)" alt="" class="icon_class" />
</el-icon>
export default ({
  name: 'menuTree',
  props: ['menuList'],
  setup(props) {
    async function sendIcon(filePath) {
      // 获取菜单图片路径
      if (filePath != null) {
        let FileInfoVo = {
          //封装请求参数
          filePath: filePath
        }
        //获取图片输出流
        let res = await getIcon(FileInfoVo);
        return URL.createObjectURL(res.data);
      }
    }

    return {
      sendIcon
    };
  }
});

注意,为了在模板中使用 await,需要将 sendIcon 函数定义为异步函数(使用 async 关键字),在调用 sendIcon 的地方使用 await 关键字等待其返回值。

NotoChen | 园豆:401 (菜鸟二级) | 2023-08-02 10:44

我修改后仍然无效,

async function sendIcon(filePath) {
      // 获取菜单图片路径
      if (filePath != null) {
        let FileInfoVo = {
          //封装请求参数
          filePath: filePath
        }
        //获取图片输出流
        let res = await getIcon(FileInfoVo)
        let a= URL.createObjectURL(res.data)
        console.log(typeof a)
        return a
      }
    }

我查看a的输出类型,是一个字符串

再看元素中src的值,依然是一个promise对象

支持(0) 反对(0) 阿宝234 | 园豆:14 (初学一级) | 2023-08-02 10:51
0

可以对img进行一次组件封装
在组件的mounted时调用getIcon,最后得到的URL.createObjectURL(res.data)绑定到组件内的img.src上,然后用这个组件替代img来使用

复制粘贴机器人 | 园豆:702 (小虾三级) | 2023-08-02 11:15
0

这应该是异步问题吧,暴力点,最简单的试试,给你那个标签加个 :key="keyNum" 变量keyNum默认是0,请求到地址后this.keyNum++ ,强行更新,你试试吧

清风引佩下瑶台 | 园豆:414 (菜鸟二级) | 2023-08-02 15:46
0

问题出现在你的sendIcon函数中。由于getIcon是一个异步请求,而sendIcon函数却立即返回一个Promise对象,导致在模板中无法正确地获取到图片的URL。

你需要在模板中使用v-if指令来控制显示图片,直到图片请求成功并获取到URL后再显示。具体做法是在组件的setup函数中定义一个ref,然后在异步请求的回调中更新这个ref,并在模板中使用v-if根据ref的值来决定是否显示图片。

修改你的代码如下:

html
Copy code
<template>
<div>
<template v-for="item in menuList" :key="item.path">
<!-- ... 省略其他代码 ... -->

  <el-icon v-if="item.iconUrl != null">
    <img v-if="iconUrls[item.iconUrl]" :src="iconUrls[item.iconUrl]" alt="" class="icon_class"/>
  </el-icon>

  <!-- ... 省略其他代码 ... -->
</template>

</div>
</template>

<script>
import { getIcon } from '@/api/home/file/index'
import { onBeforeUnmount, ref } from "vue";

export default ({
name: 'menuTree',
props: ['menuList'],
setup(props) {
const iconUrls = ref({});

function sendIcon(filePath) {
  if (filePath != null) {
    if (iconUrls.value[filePath]) {
      return iconUrls.value[filePath];
    } else {
      let FileInfoVo = {
        filePath: filePath
      }

      getIcon(FileInfoVo).then(res => {
        const url = URL.createObjectURL(res.data);
        iconUrls.value[filePath] = url;
      }).catch(err => {
        console.error("Failed to load icon:", err);
      });
    }
  }
}

onBeforeUnmount(() => {
  // 清除所有URL对象
  for (const url of Object.values(iconUrls.value)) {
    URL.revokeObjectURL(url);
  }
});

return {
  iconUrls,
  sendIcon
};

}
})
</script>
上述修改中,我们引入了ref函数,使用iconUrls来存储已经获取到的图片URL。sendIcon函数现在会先检查iconUrls中是否已经有该图片的URL,如果有则直接返回,如果没有则发起异步请求获取图片,并在回调中更新iconUrls。这样,图片的URL会在获取成功后存储在iconUrls中,而在获取之前不会在模板中显示图片。

注意:由于图片URL是根据filePath作为key存储在iconUrls中的,你需要确保item.iconUrl的值是唯一的,否则可能会出现图片错位的问题。最好是使用图片的唯一标识或ID作为filePath。

Technologyforgood | 园豆:6766 (大侠五级) | 2023-08-03 22:20

在你的修改中,sendIcon函数是怎么触发的呢?

支持(0) 反对(0) 阿宝234 | 园豆:14 (初学一级) | 2023-08-04 07:37
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册