一、检测:nginx 是否支持lua
nginx -V | grep lua
二、nginx的配置
location /proxy/ { resolver 8.8.8.8 114.114.114.114 valid=300s; resolver_timeout 10s; # 1. 用 Lua 解码 URL 参数 set_by_lua_block $target_url { local url = ngx.var.arg_url if not url or url == "" then return "" end -- 解码 URL(处理 %3A %2F 等) local decoded = ngx.unescape_uri(url) -- 验证是否是合法 URL if not decoded:match("^https?://") then return "" end return decoded } # 2. 参数验证 if ($target_url = "") { return 400 '{"error":"Missing or invalid url parameter"}'; } # 3. 可选:白名单限制(建议开启) if ($target_url !~ ^https?://([a-zA-Z0-9_-]+\.)*coze\.cn(/|$)) { return 403 '{"error":"Domain not allowed"}'; } # 4. 清除可能干扰的请求头 proxy_set_header Origin ""; proxy_set_header Referer ""; # 5. 模拟真实浏览器请求 proxy_set_header User-Agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"; proxy_set_header Accept "*/*"; proxy_set_header Accept-Language "zh-CN,zh;q=0.9,en;q=0.8"; # 6. 核心代理 proxy_pass $target_url; proxy_redirect off; proxy_set_header Host $proxy_host; # 7. SSL 配置 proxy_ssl_server_name on; proxy_ssl_protocols TLSv1.2 TLSv1.3; proxy_ssl_verify off; # 8. 视频流优化:关闭缓冲 proxy_buffering off; proxy_request_buffering off; # 9. 不强制下载(让浏览器根据 Content-Type 处理) # proxy_hide_header Content-Disposition; # add_header Content-Disposition "attachment" always; # 10. CORS 头 add_header Access-Control-Allow-Origin "*" always; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always; add_header Access-Control-Allow-Headers "*" always; add_header Access-Control-Expose-Headers "Content-Disposition, Content-Length" always; # 11. 预检请求 if ($request_method = 'OPTIONS') { return 204; } # 12. 超时设置 proxy_connect_timeout 30s; proxy_send_timeout 30s; proxy_read_timeout 60s; }
三、vue示例
// 代理请求工具函数 function proxyFetch(targetUrl) { // 必须编码,否则 & 和 = 会被当作查询参数解析 const encodedUrl = encodeURIComponent(targetUrl); return fetch(`/proxy/?url=${encodedUrl}`, { method: 'GET' }); } // 使用示例 async function loadVideo(videoUrl) { try { const response = await proxyFetch(videoUrl); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const blob = await response.blob(); const objectUrl = URL.createObjectURL(blob); const video = document.querySelector('video'); video.src = objectUrl; } catch (error) { console.error('Failed:', error); } } // 调用 const cozeVideoUrl = 'https://lf6-bot-platform-tos-sign.coze.cn/bot-studio-bot-platform/bot_files/3850925219778179/video/quicktime/7641920355644571682/7e9889f1-8059-4846-bbc1-2f1fea5ad040.mp4?x-expires=1779878095&x-signature=5mgX3n%2BmKwjzWIEP%2Ffm8xTODlZQ%3D'; loadVideo(cozeVideoUrl);