边缘安全加速平台 EO通过边缘函数实现网站自适应图片格式转换,边缘安全加速平台 EO是腾讯的EdgeOne CDN,技术宅个人觉得非常好用,今天技术宅给大家介绍边缘安全加速平台 EO通过边缘函数实现网站自适应图片格式转换,简单来说,就是通过边缘函数,实现网站图片的格式转换与防盗链。之前技术宅写过相关教程,有兴趣的可以前往。
图片自适应处理
// 浏览器图片格式优先级配置,AVIF优先 const browserFormats = { Chrome: ['avif', 'webp'], Opera: ['avif', 'webp'], Firefox: ['avif', 'webp'], Safari: ['avif', 'jp2'], Edge: ['avif', 'webp'], // Edge优先AVIF,其次WebP IE: ['jxr'] }; // 常见图片文件扩展名(增强Edge场景的覆盖) const imageExtensions = [ 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'webp', 'avif', 'jp2', 'jxr', 'svg', 'jpe', 'jfif' // 补充Edge可能请求的特殊图片扩展名 ]; // 检测浏览器是否支持指定图片格式(增强Edge版本识别) function supportsFormat(userAgent, format) { // 针对Edge的AVIF支持检测优化 if (format === 'avif') { // 匹配Edge所有版本标识(Edg/、MicrosoftEdge/等) const edgeVersionMatch = userAgent.match(/(Edg|MicrosoftEdge)\/(\d+)/i); if (edgeVersionMatch) { const version = parseInt(edgeVersionMatch[2], 10); return version >= 85; // Edge 85+支持AVIF } // 其他浏览器的AVIF检测(保持不变) if (/Chrome\/(\d+)/i.test(userAgent)) return parseInt(RegExp.$1) >= 85; if (/Firefox\/(\d+)/i.test(userAgent)) return parseInt(RegExp.$1) >= 93; if (/OPR\/(\d+)/i.test(userAgent)) return parseInt(RegExp.$1) >= 71; if (/Version\/(\d+)\.(\d+)/i.test(userAgent)) { const [major, minor] = [parseInt(RegExp.$1), parseInt(RegExp.$2)]; return major > 14 || (major === 14 && minor >= 1); } return false; } // WebP格式检测(增强Edge支持判断) if (format === 'webp') { // Edge 18+支持WebP,排除老旧版本 const edgeVersionMatch = userAgent.match(/(Edg|MicrosoftEdge)\/(\d+)/i); if (edgeVersionMatch) { const version = parseInt(edgeVersionMatch[2], 10); return version >= 18; } return !/Trident|MSIE/i.test(userAgent); // 排除IE } return true; } // 检查是否为图片请求(增强Edge场景的识别) function isImageRequest(url, acceptHeader, userAgent) { // 1. 扩展名匹配(补充Edge常见图片格式) const extMatch = url.pathname.match(/\.([^.]+)$/); if (extMatch) { const ext = extMatch[1].toLowerCase(); if (imageExtensions.includes(ext)) return true; } // 2. Accept头匹配(增强Edge的图片类型识别) if (acceptHeader.includes('image') || acceptHeader.includes('webp') || // Edge可能单独声明WebP acceptHeader.includes('avif')) { // Edge可能单独声明AVIF return true; } // 3. 针对Edge动态加载图片的特殊处理(无扩展名但为图片) if (userAgent.includes('edg') && url.pathname.includes('/image/')) { return true; } return false; } addEventListener('fetch', event => { event.passThroughOnException(); event.respondWith(handleEvent(event)); }); async function handleEvent(event) { const { request } = event; if (request.method !== 'GET') return fetch(request); const userAgent = (request.headers.get('user-agent') || '').toLowerCase(); const acceptHeader = (request.headers.get('accept') || '').toLowerCase(); const url = new URL(request.url); // 增强Edge的图片请求识别(传入userAgent辅助判断) if (!isImageRequest(url, acceptHeader, userAgent)) { console.log(`Edge未转换:非图片请求 | URL=${url.href} | UA=${userAgent}`); return fetch(request); } // 浏览器识别(优化Edge的匹配规则) const browser = getBrowser(userAgent); if (!browser) { console.log(`Edge未转换:未识别浏览器 | UA=${userAgent}`); return fetch(request); } // 记录Edge的识别结果 if (browser === 'Edge') { console.log(`识别到Edge浏览器 | UA=${userAgent}`); } // 确定最佳格式(优先AVIF) const preferredFormats = browserFormats[browser] || []; let targetFormat = null; for (const format of preferredFormats) { if (supportsFormat(userAgent, format)) { targetFormat = format; break; } } // 无支持格式时的日志(便于排查Edge不转换原因) if (!targetFormat) { console.log(`Edge未转换:不支持任何格式 | UA=${userAgent} | 支持格式=${preferredFormats.join(',')}`); return fetch(request); } // 执行转换(增强Edge的请求处理) try { const modifiedRequest = new Request(request, { headers: new Headers(request.headers) }); // 针对Edge添加特殊请求头,确保转换生效 modifiedRequest.headers.set('X-Edge-Client', 'true'); const response = await fetch(modifiedRequest, { eo: { image: { format: targetFormat, // 针对Edge优化转换参数(如压缩质量) quality: 85 // 平衡质量和兼容性 } } }); const newResponse = new Response(response.body, response); newResponse.headers.set('X-Image-Format', targetFormat); newResponse.headers.set('X-Image-Converter', 'EdgeOne'); newResponse.headers.set('Cache-Control', 'public, max-age=86400, stale-while-revalidate=43200'); console.log(`Edge转换成功:${targetFormat} | URL=${url.href} | UA=${userAgent}`); return newResponse; } catch (err) { console.error(`Edge转换失败:${err.message} | URL=${url.href} | UA=${userAgent}`); return fetch(request); } } // 浏览器检测函数(增强Edge的识别准确性) function getBrowser(userAgent) { // 优先匹配Edge所有可能的标识(覆盖新旧版本) if (/Edg\/|Edge\/|EdgA\/|Edgios\/|EdgWP\/|MicrosoftEdge/i.test(userAgent)) { return 'Edge'; } // 其他浏览器检测(保持不变) if (/Trident|MSIE|Internet Explorer/i.test(userAgent)) return 'IE'; if (/Firefox|FxiOS/i.test(userAgent) && !/Seamonkey/i.test(userAgent)) return 'Firefox'; if (/Chrome|CriOS/i.test(userAgent) && !/Edg|Edge|OPR|Opera/i.test(userAgent)) return 'Chrome'; if (/Opera|OPR|OPiOS/i.test(userAgent)) return 'Opera'; if (/Safari|AppleWebKit/i.test(userAgent) && !/Chrome|Edg|Edge|OPR|Opera|Firefox|FxiOS/i.test(userAgent)) return 'Safari'; return null; }
防盗链
const fileType = ['jpg', 'gif', 'png', 'webp', 'js', 'css', 'ico', 'woff', 'woff2', 'jpeg', 'avif']; // 白名单域名列表 - 包含网站域名和小程序域名(新增百度小程序相关域名) const whitelistHosts = [ 'https://thax.cn', 'https://wei.thax.cn', 'https://servicewechat.com', 'https://smartapp.baidu.com', 'https://baiduboxapp.com', // 百度 App 相关域名 'https://bdimg.share.baidu.com' // 百度分享相关域名 ]; // 搜索引擎识别规则(涵盖主流搜索引擎的域名和爬虫标识) const searchEnginePatterns = [ // 百度系搜索引擎及爬虫 /baidu.com/, /baiduspider/, // 谷歌系搜索引擎及爬虫 /google.com/, /googlebot/, // 必应搜索引擎及爬虫 /bing.com/, /bingbot/, // 搜狗搜索引擎及爬虫 /sogou.com/, /sogoubot/, // 360 搜索引擎及爬虫 /so.360.cn/, /360spider/, // 头条搜索引擎及爬虫 /bytedance.com/, /toutiaospider/ ]; // 百度小程序特殊标识检测 function isBaiduMiniProgram(request) { const userAgent = (request.headers.get('User-Agent') || '').toLowerCase(); const referer = request.headers.get('Referer') || ''; // 百度小程序常见标识 const baiduMiniProgramPatterns = [ /swan\/\d+/, // 百度智能小程序标识 /baiduboxapp/, // 百度 App 标识 /miniprogram/ // 通用小程序标识 ]; return baiduMiniProgramPatterns.some(pattern => pattern.test(userAgent) || pattern.test(referer) ); } async function handleEvent(event) { const { request } = event; const urlInfo = new URL(request.url); // 1. 不需要校验的文件类型,直接放行 const extMatch = urlInfo.pathname.match(/\.([^.]+)$/); const ext = extMatch ? extMatch[1].toLowerCase() : ''; if (!ext || !fileType.includes(ext)) { return; } // 2. 获取关键请求头 const referer = request.headers.get('Referer') || ''; const userAgent = (request.headers.get('User-Agent') || '').toLowerCase(); const blockRes = new Response(null, { status: 404 }); // 3. 优先检测百度小程序(新增逻辑) if (isBaiduMiniProgram(request)) { console.log(`Allowing Baidu mini program request: Referer=${referer}, UA=${userAgent}`); return; } // 4. 搜索引擎识别逻辑 const isSearchEngine = searchEnginePatterns.some(pattern => pattern.test(referer)) || searchEnginePatterns.some(pattern => pattern.test(userAgent)); if (isSearchEngine) { console.log(`Allowing search engine request: Referer=${referer}, UA=${userAgent}`); return; } // 5. 非搜索引擎请求的常规校验 if (!referer) { return event.respondWith(blockRes); } // 检查 Referer 是否在白名单中 const isWhitelisted = whitelistHosts.some(domain => { return referer.startsWith(domain); }); if (!isWhitelisted) { console.log(`Referer ${referer} is not in whitelist, blocking`); return event.respondWith(blockRes); } // 校验通过,直接放行 return; } addEventListener('fetch', event => { event.passThroughOnException(); handleEvent(event); });
以上代码大家自行测试,有问题可以留言。