问题描述:后端响应文件输出流,前端采用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对象
你在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方法用于发送获取图片流的请求,你需要根据实际情况进行实现。
通过这种方式,你应该能够正确地在模板中显示图片了。
问题在于 sendIcon
函数中,虽然你使用了 async
和 await
,但是实际上 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
关键字等待其返回值。
我修改后仍然无效,
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对象
可以对img进行一次组件封装
在组件的mounted时调用getIcon
,最后得到的URL.createObjectURL(res.data)
绑定到组件内的img.src上,然后用这个组件替代img来使用
这应该是异步问题吧,暴力点,最简单的试试,给你那个标签加个 :key="keyNum" 变量keyNum默认是0,请求到地址后this.keyNum++ ,强行更新,你试试吧
问题出现在你的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。
在你的修改中,sendIcon函数是怎么触发的呢?