Browse Source

fix: 优化纯 JS 跨域抓 HTML 的代理方案,增加多代理 fallback 机制

lsc 4 days ago
parent
commit
65918e0298
1 changed files with 29 additions and 15 deletions
  1. 29 15
      src/hooks/useImport.ts

+ 29 - 15
src/hooks/useImport.ts

@@ -1,4 +1,4 @@
-import { ref, nextTick } from 'vue'
+import { ref, nextTick } from 'vue'
 import { storeToRefs } from 'pinia'
 import { parse, type Shape, type Element, type ChartItem, type BaseElement } from 'pptxtojson'
 import { nanoid } from 'nanoid'
@@ -1954,19 +1954,22 @@ export default () => {
     })
   }
 
-  // 纯 JS 跨域抓 HTML(最后兜底方案):通过公共 CORS 代理 allorigins 获取目标页面源码
+  // 纯 JS 跨域抓 HTML(最后兜底方案):通过公共 CORS 代理获取目标页面源码
   // 用于 type=73 在 getHTML/getFile/getFile2 全部失败后的兜底
-  const getHtmlPureJS = (url: string): Promise<string> => {
+  // 公共代理可用性不稳定(尤其国内网络),依次尝试多个,一个失败换下一个
+  const CORS_PROXIES: Array<(url: string) => string> = [
+    url => 'https://api.allorigins.win/raw?url=' + encodeURIComponent(url),
+    url => 'https://corsproxy.io/?url=' + encodeURIComponent(url),
+    url => 'https://api.codetabs.com/v1/proxy?quest=' + encodeURIComponent(url),
+  ]
+
+  const fetchViaProxy = (proxyFn: (url: string) => string, url: string): Promise<string> => {
     return new Promise((resolve, reject) => {
       const xhr = new XMLHttpRequest()
+      xhr.open('GET', proxyFn(url), true)
+      xhr.timeout = 12000 // iPad WebView 可能挂起不回调,必须加超时
 
-      // 关键:必须用公共 CORS 代理,否则低版本 WebView 跨域直接拦截
-      const proxyUrl = 'https://api.allorigins.win/raw?url=' + encodeURIComponent(url)
-
-      xhr.open('GET', proxyUrl, true)
-      xhr.timeout = 15000 // iPad WebView 可能挂起不回调,必须加超时
-
-      xhr.onreadystatechange = function () {
+      xhr.onreadystatechange = function() {
         if (xhr.readyState === 4) {
           if (xhr.status === 200 && xhr.responseText) {
             resolve(xhr.responseText)
@@ -1977,19 +1980,30 @@ export default () => {
           }
         }
       }
-
-      xhr.ontimeout = function () {
+      xhr.ontimeout = function() {
         reject(new Error('Request Timeout'))
       }
-
-      xhr.onerror = function () {
+      xhr.onerror = function() {
         reject(new Error('Network Error'))
       }
-
       xhr.send()
     })
   }
 
+  const getHtmlPureJS = async (url: string): Promise<string> => {
+    let lastError: unknown = null
+    for (const proxyFn of CORS_PROXIES) {
+      try {
+        return await fetchViaProxy(proxyFn, url)
+      }
+      catch (err) {
+        console.warn('CORS 代理失败,尝试下一个:', proxyFn(url).slice(0, 50), err)
+        lastError = err
+      }
+    }
+    throw lastError || new Error('All proxies failed')
+  }
+
   return {
     importSpecificFile,
     importJSON,