You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
4.5 KiB
200 lines
4.5 KiB
|
3 months ago
|
// 图片URL验证工具
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 验证图片URL是否有效
|
||
|
|
* @param {string} url - 图片URL
|
||
|
|
* @returns {boolean} - 是否为有效图片URL
|
||
|
|
*/
|
||
|
|
function isValidImageUrl(url) {
|
||
|
|
if (!url || typeof url !== 'string') {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 允许各种合法的图片URL格式
|
||
|
|
// 1. 标准HTTP/HTTPS URL
|
||
|
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. 阿里云OSS临时URL(可能包含特殊字符)
|
||
|
|
if (url.includes('oss-cn-') && url.includes('aliyuncs.com')) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 3. 小程序本地临时文件路径
|
||
|
|
if (url.startsWith('wxfile://')) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 4. Base64编码的图片数据URL
|
||
|
|
if (url.startsWith('data:image/')) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 5. 占位符URL,在实际场景中会被替换
|
||
|
|
if (url.startsWith('placeholder://')) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 过滤有效的图片URL
|
||
|
|
* @param {Array<string>} urls - 图片URL数组
|
||
|
|
* @returns {Array<string>} - 有效的图片URL数组
|
||
|
|
*/
|
||
|
|
function filterValidImageUrls(urls) {
|
||
|
|
if (!Array.isArray(urls)) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
return urls.filter(url => isValidImageUrl(url));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 修复商品对象中的图片URL
|
||
|
|
* @param {Object} product - 商品对象
|
||
|
|
* @returns {Object} - 修复后的商品对象
|
||
|
|
*/
|
||
|
|
function fixProductImageUrls(product) {
|
||
|
|
if (!product || typeof product !== 'object') {
|
||
|
|
return product;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 创建商品的副本以避免直接修改原对象
|
||
|
|
const fixedProduct = { ...product };
|
||
|
|
|
||
|
|
// 修复主图片URL数组
|
||
|
|
if (fixedProduct.imageUrls && Array.isArray(fixedProduct.imageUrls)) {
|
||
|
|
fixedProduct.imageUrls = filterValidImageUrls(fixedProduct.imageUrls);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 修复备用图片URL数组(如果存在)
|
||
|
|
if (fixedProduct.allImageUrls && Array.isArray(fixedProduct.allImageUrls)) {
|
||
|
|
fixedProduct.allImageUrls = filterValidImageUrls(fixedProduct.allImageUrls);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 修复单个图片URL(如果存在)
|
||
|
|
if (fixedProduct.imageUrl && !isValidImageUrl(fixedProduct.imageUrl)) {
|
||
|
|
fixedProduct.imageUrl = '';
|
||
|
|
}
|
||
|
|
|
||
|
|
return fixedProduct;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 修复商品列表中的图片URL
|
||
|
|
* @param {Array<Object>} products - 商品列表
|
||
|
|
* @returns {Array<Object>} - 修复后的商品列表
|
||
|
|
*/
|
||
|
|
function fixProductListImageUrls(products) {
|
||
|
|
if (!Array.isArray(products)) {
|
||
|
|
return products;
|
||
|
|
}
|
||
|
|
|
||
|
|
return products.map(product => fixProductImageUrls(product));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 标准化图片URL格式
|
||
|
|
* @param {string} url - 图片URL
|
||
|
|
* @returns {string} - 标准化后的图片URL
|
||
|
|
*/
|
||
|
|
function normalizeImageUrl(url) {
|
||
|
|
if (!url || typeof url !== 'string') {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
// 去除URL前后的空格
|
||
|
|
const trimmedUrl = url.trim();
|
||
|
|
|
||
|
|
// 对于占位符URL,不进行任何额外处理,直接返回
|
||
|
|
if (trimmedUrl.startsWith('placeholder://')) {
|
||
|
|
return trimmedUrl;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 确保URL格式正确
|
||
|
|
if (trimmedUrl.startsWith('//')) {
|
||
|
|
return 'https:' + trimmedUrl;
|
||
|
|
}
|
||
|
|
|
||
|
|
return trimmedUrl;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 标准化图片URL数组
|
||
|
|
* @param {Array<string>} urls - 图片URL数组
|
||
|
|
* @returns {Array<string>} - 标准化后的图片URL数组
|
||
|
|
*/
|
||
|
|
function normalizeImageUrls(urls) {
|
||
|
|
if (!Array.isArray(urls)) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
return urls.map(url => normalizeImageUrl(url)).filter(url => url !== '');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取图片URL验证统计信息
|
||
|
|
* @param {Array<string>} urls - 图片URL数组
|
||
|
|
* @returns {Object} - 验证统计信息
|
||
|
|
*/
|
||
|
|
function getImageUrlStats(urls) {
|
||
|
|
const stats = {
|
||
|
|
total: 0,
|
||
|
|
valid: 0,
|
||
|
|
invalid: 0,
|
||
|
|
types: {
|
||
|
|
http: 0,
|
||
|
|
https: 0,
|
||
|
|
oss: 0,
|
||
|
|
wxfile: 0,
|
||
|
|
data: 0,
|
||
|
|
placeholder: 0,
|
||
|
|
other: 0
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
if (!Array.isArray(urls)) {
|
||
|
|
return stats;
|
||
|
|
}
|
||
|
|
|
||
|
|
stats.total = urls.length;
|
||
|
|
|
||
|
|
urls.forEach(url => {
|
||
|
|
if (isValidImageUrl(url)) {
|
||
|
|
stats.valid++;
|
||
|
|
|
||
|
|
if (url.startsWith('http://')) {
|
||
|
|
stats.types.http++;
|
||
|
|
} else if (url.startsWith('https://')) {
|
||
|
|
stats.types.https++;
|
||
|
|
} else if (url.includes('oss-cn-') && url.includes('aliyuncs.com')) {
|
||
|
|
stats.types.oss++;
|
||
|
|
} else if (url.startsWith('wxfile://')) {
|
||
|
|
stats.types.wxfile++;
|
||
|
|
} else if (url.startsWith('data:image/')) {
|
||
|
|
stats.types.data++;
|
||
|
|
} else if (url.startsWith('uploaded_')) {
|
||
|
|
stats.types.placeholder++;
|
||
|
|
} else {
|
||
|
|
stats.types.other++;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
stats.invalid++;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
return stats;
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
isValidImageUrl,
|
||
|
|
filterValidImageUrls,
|
||
|
|
fixProductImageUrls,
|
||
|
|
fixProductListImageUrls,
|
||
|
|
normalizeImageUrl,
|
||
|
|
normalizeImageUrls,
|
||
|
|
getImageUrlStats
|
||
|
|
};
|