1.问题背景
项目中图片上传的功能,需要在el-table中展示,并且点击编辑需要在el-upload中重新展示上传的图片; 图片并不是保存在数据库中,而是保存在miniIO中(miniIO是一个文件存储服务器,文件上传后存储在根据日期划分的文件夹中,比如/20250331/abc123_fhgjiouyyAdf-ffeegkki.jpg),文件上传成功后,接口返回的是类似这样的数据
js
{
code:'0000',
data:{
fileName:'1.jpg',
url:'/20250331/abc123_fhgjiouyyAdf-ffeegkki.jpg'
}
}
意思就是接口只返回文件在miniIO中地址; 现在图片上传成功在表格中创建一条数据后,在表格中无法展示,并且点击编辑,el-upload组件汇总无法回显图片;
2.原因分析
(1)表格中无法展示图片 表格中使用el-image标签展示图片
html
<template slot-scope="scope">
<el-image :src="scope.row.url" style="width:50px;height:50px;" fit="contain"/>
</template>
在js代码中打印url的格式是
js
http://localhost/data/jjjj/20250331/abc123_fhgjiouyyAdf-ffeegkki.jpg
明显请求的路径地址不对,本地调试的时候需要代理到服务器地址 (2)图片请求需要携带headers 由于后端表格数据返回的只是图片在miniIO上的文件地址,而不是一个blob类型的数据,所以想要获取图片,其实还需要再次请求后端的地址来获取blob类型的数据; el-image的底层使用的还是img标签,当img标签的src属性中是一个图片地址的时候(网络地址),它会通过浏览器向这个网络地址发起一个请求,请求图片的资源; 但是后端为了安全,对每个请求做了限制,需要在headers中携带Authorization,但是img标签请求图片资源是浏览器控制的,它不会经过axios,所以在axios的请求拦截器中添加的headers操作不会实现在img请求中; (3)el-upload无法复现图片 el-upload组件有一个属性header,但是它只能在发起上传图片请求的时候携带在请求中,复现图片主要依靠的是将file放入file-list中,这样可以在el-ipload中展示图片; el-upload底层也是使用的img标签,这样和上一个原因一样,也是因为再次请求图片资源的时候没有携带Authorization导致图片资源没有下载下来导致无法展示;
3.解决办法
(1)后端修改对图片资源请求接口的限制
可以做,但是不能做,原因都懂的
(2)使用serviceWorker来代理img请求
有限制:serviceWorker只能在 http://localhost 和 https:// 中使用,这意味着当你的项目线上环境的访问ip是http开头的时候,这种方式就不能使用; 1)新建serviceWorker文件:public/sw.js
js
// sw.js
let authToken = null;
self.addEventListener('message', (event) => {
if (event.data.type === 'SET_TOKEN' || event.data.type === 'UPDATE_TOKEN') {
authToken = event.data.token;
}
});
self.addEventListener('fetch', (event) => {
if (authToken && event.request.url.endsWith('.jpg')) {
const newHeaders = new Headers(event.request.headers);
newHeaders.set('Authorization', `Bearer ${authToken}`);
event.respondWith(
fetch(new Request(event.request, { headers: newHeaders }))
);
}
});
2)在msin.js中注册serviceWorker并且传递token
javascript
if ('serviceWorker' in navigator) {
// 注册时传递 Token
navigator.serviceWorker.register('/sw.js').then(registration => {
// 从存储中获取 Token
const token = localStorage.getItem('auth_token') ||
getCookie('auth_token');
// 发送给 Service Worker
registration.active?.postMessage({
type: 'SET_TOKEN',
token
});
});
// 监听存储变化实时更新
window.addEventListener('storage', () => {
navigator.serviceWorker.controller?.postMessage({
type: 'UPDATE_TOKEN',
token: localStorage.getItem('auth_token')
});
});
}
(3)笨办法:在url放入img标签前重新请求图片资源
1)在el-table中,获取到表格数据后,取出每条数据中的url字段,使用axios、fetch、XMLHttpRequest进行请求,封装一个requestImgUrl方法
js
async function requestImgUrl(url){
const response =await axios.get(
url:url,
{
responseType:'blob',
headers:{
Authorization:"bearer"+getToken(),//getToken是封装的获取Cookies或者sessionStorage中存储的token的方法
}
}
)
return URL.createObjectURL(response.data)
}
在获取表格数据时进行遍历
js
for(let item of res.data)
item.url = await requestImgUrl(item.url)
}
2)el-upload中也是同样的方法,这是需要放入file-list中
html
<el-upload
...
:file-list="fileList"
...
/>
js
async function editData(row){
...
if(row.url){
let blobData = await requestImgUrl(row.url)
fileList.vlaue = [{
url:blobData
}]
}
...
}