问题出现的背景:前端要调用后端的一个方法https://api.crossbiog.com/brand/admin/detail/?id=78获取introduceAttachmentList数组中的url,但是获取到这个"url": “56/brand/旭包鲜轮播图1750375_696868.jpg”,后,拼接基础https://www.crossbiog.com/的地址得到一个完整的地址https://www.crossbiog.com/56/brand/%E6%97%AD%E5%8C%85%E9%B2%9C%E8%BD%AE%E6%92%AD%E5%9B%BE1750375_696868.jpg
但是发现这个地址被服务器拒接访问,因为需要签名认证参数(Expires、OSSAccessKeyId、Signature)阿里云服务器oss才可以访问图片,这个时候就得生成签名认证,最后通过寻找后端代码找了好久才找到这个方法,可以返回签名认证。
{
"code": 0,
"msg": "成功",
"data": {
"id": 78,
"region": 3,
"code": "B240910204913-5257",
"logo": "brand-logo/旭包鲜LOGO_803497.jpg",
"name": "旭包鲜",
"englishName": "AsahiKASEI",
"location": "日本",
"introduce": "旭包鲜®是具有代表性的日本综合化学品生产企业—旭化成集团旗下的旭化成家庭用品株式会社所持有的厨房用品品牌。它所生产的保鲜膜日语名称为「サランラップ®」在日本已经畅销超过60年以上。 PVDC材质的保鲜膜以其优良的密封性、可微波炉加热、结实易撕等高性能品质,在日本是享有盛誉的长期畅销产品。2001年开始正式进入中国市场。现在,在中国的各大实体超市以及网购平台均有销售。",
"ranks": 0,
"nameList": [
],
"logoAttachmentList": [
],
"introduceAttachmentList": [
{
"name": "旭包鲜轮播图1750375_696868.jpg",
"url": "56/brand/旭包鲜轮播图1750375_696868.jpg",
"size": 225136,
"sizeStr": "219.86K",
"thumbnail": "56/brand/旭包鲜轮播图1750375_696868-thumbnail.jpg",
"width": null,
"height": null
},
{
"name": "旭包鲜轮播图2750375_435717.jpg",
"url": "56/brand/旭包鲜轮播图2750375_435717.jpg",
"size": 225136,
"sizeStr": "219.86K",
"thumbnail": "56/brand/旭包鲜轮播图2750375_435717-thumbnail.jpg",
"width": null,
"height": null
},
{
"name": "旭包鲜轮播图3750375_922227.jpg",
"url": "56/brand/旭包鲜轮播图3750375_922227.jpg",
"size": 225136,
"sizeStr": "219.86K",
"thumbnail": "56/brand/旭包鲜轮播图3750375_922227-thumbnail.jpg",
"width": null,
"height": null
},
{
"name": "旭包鲜轮播图4750375_218746.jpg",
"url": "56/brand/旭包鲜轮播图4750375_218746.jpg",
"size": 225136,
"sizeStr": "219.86K",
"thumbnail": "56/brand/旭包鲜轮播图4750375_218746-thumbnail.jpg",
"width": null,
"height": null
},
{
"name": "旭包鲜轮播图5750375_774655.jpg",
"url": "56/brand/旭包鲜轮播图5750375_774655.jpg",
"size": 225136,
"sizeStr": "219.86K",
"thumbnail": "56/brand/旭包鲜轮播图5750375_774655-thumbnail.jpg",
"width": null,
"height": null
}
],
"editAuth": 1
}
}
https://www.crossbiog.com/56/brand/%E6%97%AD%E5%8C%85%E9%B2%9C%E8%BD%AE%E6%92%AD%E5%9B%BE1750375_696868.jpg
这个地址不可以访问阿里云oss,这个地址从后端获取的其中一部分56/brand/旭包鲜轮播图1750375_696868.jpg然后拼接而成的。
https://www.crossbiog.com//56/brand/%E6%97%AD%E5%8C%85%E9%B2%9C%E8%BD%AE%E6%92%AD%E5%9B%BE1750375_696868.jpg?
Expires=1730198146&OSSAccessKeyId=LTAI4FwmPQBRwQf7fJpXXJEo&Signature=REz3gWtn8e4K1bDnho9%2FP5JxnVc%3D
这个地址就可以访问阿里云oss
两个链接的主要区别在于第二个链接中包含了一些查询参数(Expires
、OSSAccessKeyId
、Signature
),这些参数通常是用于访问云存储服务(如阿里云OSS)时的签名认证信息,用于验证请求的有效性和安全性。
为什么第一个链接无法访问,而第二个可以:
-
签名认证:
- 第二个链接包含了签名认证参数(
Expires
、OSSAccessKeyId
、Signature
),这些参数使得链接能够通过服务器的认证,因此可以访问。 - 第一个链接没有包含这些认证参数,因此无法通过认证,导致无法访问。
- 第二个链接包含了签名认证参数(
-
权限问题:
- 没有签名的链接可能没有权限访问资源,特别是当资源存储在需要权限验证的云存储服务上时。
-
链接格式:
- 第二个链接中的额外斜杠(
//
)可能是一个错误,但这并不影响链接的功能,因为URL解析器会自动处理多余的斜杠。
- 第二个链接中的额外斜杠(
解决方法:
-
检查链接:
- 确保链接的格式正确,特别是签名参数是否完整且有效。
-
获取有效的签名:
- 如果你是资源的拥有者或有权访问资源,确保生成有效的签名参数,并将其添加到链接中。
-
联系资源提供者:
- 如果你无法生成签名,可能需要联系资源的提供者获取正确的链接。
-
检查网络连接:
- 确保网络连接正常,有时网络问题也可能导致链接无法访问。
-
重试链接:
- 有时服务器可能暂时不可用,稍后重试可能会解决问题。
const { imageBaseUrl } = require('../../config/config');
const config = require('../../config/config');
Page({
data: {
swiperImages:[
],// 初始化轮播图数据
brand: {},
showBottomImage: true
},
// 获取 token
getToken: function() {
return new Promise((resolve,reject)=>{
// 模拟登录获取 token
// wx.request({
// url: config.baseUrl + config.signInUrl, // 使用配置文件中的URL
// method: 'POST',
// data: {
// username: '18661977581', // 替换为您的用户名
// password: 'XXXXX' // 替换为您的密码
// },
// header: {
// 'content-type': 'application/x-www-form-urlencoded' // 设置请求头为表单格式
// },
// success: (res) => {
// console.log("res="+res)
// console.log("res.statusCode="+res.statusCode)
// console.log("res.data = " + res.data);
// console.log("res.data = " + JSON.stringify(res.data));
// // 检查返回的数据结构
// if (res.statusCode === 200 && res.data && res.data.data && res.data.data.token) {
// let token = res.data.data.token;
// console.log("后端获取到的token="+token)
// wx.setStorageSync('token', token);
// resolve(token)
// } else {
// reject(new Error('获取token失败,响应数据不符合预期'));
// }
// },
// fail: (err) => {
// reject(new Error('请求失败'));
// }
// });
const token = wx.getStorageSync('token')
resolve(token)
});
},
// 发送请求获取品牌数据
fetchBrandData: function() {
this.getToken().then((token) => {
console.log("获取品牌数据前需要携带token=" + token);
if (!token) {
wx.showToast({
title: '获取 token 失败,请重试',
icon: 'none'
});
return;
}
function requestGeneral(url,method,data={},header={}){
return new Promise((resolve,reject)=>{
wx.request({
url: config.baseUrl + url,
method:method,
data:data,
header:{
...header,
'content-type':'application/x-www-form-urlencoded',
},
success:(res)=>{
if(res.statusCode === 200){
resolve(res.data);
}else{
reject(new Error('请求失败'));
}
},
fail:(err)=>{
reject(new Error('请求失败'));
}
})
})
}
requestGeneral(config.getBrandDetailUrl,'GET', {page:0,size:10}, { 'token': token })
.then((res) => {
console.log("then(res):",res)
if (res.code === 0) {
console.log("res.code=",res.code)
const data = res.data || {};
console.log("res.data=",data)
const content = res.data.content;
console.log("content="+content)
const brand = content[0];
this.setData({
brand: brand,
});
} else {
wx.showToast({
title: '数据加载失败',
icon: 'none'
});
}
})
.catch((err) => {
wx.showToast({
title: '请求失败',
icon: 'none'
});
});
})
.catch((err) => {
wx.showToast({
title: err.message,
icon: 'none'
});
});
},
// 构建查询参数字符串
buildQueryParams: function(params) {
return Object.keys(params).map(key => `${key}=${encodeURIComponent(params[key])}`).join('&');
},
// 发送请求获取轮播图数据
fetchSwiperImages: function() {
this.getToken().then((token) => {
function requestGeneral(url,method,data={},header={}){
return new Promise((resolve,reject)=>{
wx.request({
url: config.baseUrl + url,
method:method,
data:data,
header:{
...header,
'content-type':'application/x-www-form-urlencoded',
},
success:(res)=>{
if(res.statusCode === 200){
resolve(res.data);
}else{
reject(new Error('请求失败'));
}
},
fail:(err)=>{
reject(new Error('请求失败'));
}
})
})
}
const url = config.getSwiperImagesUrl; // 不再替换路径参数
const id = { id: 78 }; // 使用查询参数
// console.log("请求轮播图数据的URL:", config.baseUrl + url + '?' + this.buildQueryParams(id));
requestGeneral(url, 'GET', id, { 'token': token })
.then((res) => {
console.log("fetchSwiperImages then(res):", res);
if (res.code === 0) {
const data = res.data || {};
const swiperImages = data.introduceAttachmentList ? data.introduceAttachmentList.map(item =>imageBaseUrl+ item.url) : [];
console.log("这个图片地址没有加密,不能访问"+swiperImages)
const introduceAttachmentListUrl = data.introduceAttachmentList ? data.introduceAttachmentList.map(item => item.url) : [];
console.log("introduceAttachmentListUrl"+introduceAttachmentListUrl)
// 请求后端生成签名 URL
const fetchSignedUrls = (urls) => {
console.log("urls:"+urls)
return Promise.all(urls.map(url => {
console.log("请求签名 URL 的资源:", url); // 输出当前请求的 URL
return new Promise((resolve, reject) => {
wx.request({
// url: 'http://localhost:8087/oss/download/',
url: config.baseUrl +'/oss/download/',
method: 'GET',
data: { url: url },
header: {
'token':token,
'Content-Type': 'application/json'
},
success: (res) => {
console.log("res.data:"+res.data)
if (res.statusCode === 200 ) {
resolve(res.data.data);
console.log("res.data.data:"+res.data.data)
} else {
reject(new Error('签名 URL 响应数据不符合预期,响应数据:' + JSON.stringify(res.data)));
}
},
fail: (err) => {
reject(new Error('请求失败', err));
}
});
});
}));
};
// fetchSignedUrls(introduceAttachmentListUrl)
// .then((signedUrls) => {
// this.setData({
// swiperImages: config.imageBaseUrl + signedUrls
// });
// })
// .catch((err) => {
// console.error("获取签名 URL 失败:", err);
// wx.showToast({
// title: '轮播图数据加载失败',
// icon: 'none'
// });
// });
fetchSignedUrls(introduceAttachmentListUrl)
.then((signedUrls) => {
// 将签名 URL 与 base URL 拼接
const completeUrls = signedUrls.map(signedUrl => config.imageBaseUrl + signedUrl);
this.setData({
swiperImages: completeUrls
});
})
.catch((err) => {
console.error("获取签名 URL 失败:", err);
wx.showToast({
title: '轮播图数据加载失败',
icon: 'none'
});
});
} else {
wx.showToast({
title: '轮播图数据加载失败',
icon: 'none'
});
}
})
.catch((err) => {
wx.showToast({
title: '请求失败',
icon: 'none'
});
});
})
},
// 页面加载时调用
onLoad: function() {
console.log("页面加载"); // 添加日志输出
this.fetchBrandData(); // 加载品牌数据
this.fetchSwiperImages();
}
});