Browse Source

fix(frame): 增加跨域抓取 HTML 的兜底方案

在 iframe 加载失败时,新增 getHtmlPureJS 方法作为兜底,通过 CORS 代理获取页面源码,解决低版本 WebView 跨域拦截问题。同时修改 fetch 请求配置 CORS 模式,优化抓取失败后的错误处理流程。
lsc 4 days ago
parent
commit
bb44d4f70b

+ 38 - 1
src/hooks/useImport.ts

@@ -1934,7 +1934,7 @@ export default () => {
     return new Promise((resolve, reject) => {
     return new Promise((resolve, reject) => {
       console.log('直接使用原始 URL 获取文件:', url)
       console.log('直接使用原始 URL 获取文件:', url)
 
 
-      fetch(url)
+      fetch(url, {mode: 'cors'})
         .then(response => {
         .then(response => {
           if (!response.ok) {
           if (!response.ok) {
             console.error('HTTP 错误:', response.status, response.statusText)
             console.error('HTTP 错误:', response.status, response.statusText)
@@ -1954,6 +1954,42 @@ export default () => {
     })
     })
   }
   }
 
 
+  // 纯 JS 跨域抓 HTML(最后兜底方案):通过公共 CORS 代理 allorigins 获取目标页面源码
+  // 用于 type=73 在 getHTML/getFile/getFile2 全部失败后的兜底
+  const getHtmlPureJS = (url: string): Promise<string> => {
+    return new Promise((resolve, reject) => {
+      const xhr = new XMLHttpRequest()
+
+      // 关键:必须用公共 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 () {
+        if (xhr.readyState === 4) {
+          if (xhr.status === 200 && xhr.responseText) {
+            resolve(xhr.responseText)
+          }
+          else if (xhr.status !== 0) {
+            // status=0 是网络错误,交给 onerror 处理,避免重复 reject
+            reject(new Error('HTTP Error: ' + xhr.status))
+          }
+        }
+      }
+
+      xhr.ontimeout = function () {
+        reject(new Error('Request Timeout'))
+      }
+
+      xhr.onerror = function () {
+        reject(new Error('Network Error'))
+      }
+
+      xhr.send()
+    })
+  }
+
   return {
   return {
     importSpecificFile,
     importSpecificFile,
     importJSON,
     importJSON,
@@ -1964,6 +2000,7 @@ export default () => {
     imgExporting,
     imgExporting,
     getFile,
     getFile,
     getFile2,
     getFile2,
+    getHtmlPureJS,
     dataToFile,
     dataToFile,
     uploadFileToS3,
     uploadFileToS3,
     getPPTInfo
     getPPTInfo

+ 11 - 3
src/views/components/element/FrameElement/BaseFrameElement.vue

@@ -132,7 +132,7 @@ import message from '@/utils/message'
 
 
 const slidesStore = useSlidesStore()
 const slidesStore = useSlidesStore()
 const manager = useVideoManager()
 const manager = useVideoManager()
-const { getFile, getFile2 } = useImport()
+const { getFile, getFile2, getHtmlPureJS } = useImport()
 
 
 const props = defineProps({
 const props = defineProps({
   elementInfo: {
   elementInfo: {
@@ -375,8 +375,16 @@ const handleIframeLoad = async (event: Event) => {
               if (fd2?.data) html = new TextDecoder('utf-8').decode(new Uint8Array(fd2.data))
               if (fd2?.data) html = new TextDecoder('utf-8').decode(new Uint8Array(fd2.data))
             }
             }
             catch (e3) {
             catch (e3) {
-              message.warning('type=73 抓取全部失败:' + e3)
-              console.error('type=73 抓取全部失败:', e3)
+              message.info('getFile2 失败,尝试 getHtmlPureJS' + e3)
+              // 最后兜底:纯 JS + CORS 代理抓取
+              // 不用在这里注入 <base>:processedSrcDoc 会根据 curl 统一注入
+              try {
+                html = await getHtmlPureJS(info.url)
+              }
+              catch (error) {
+                message.warning('type=73 抓取全部失败:' + error)
+                console.error('type=73 抓取全部失败:', e3, error)
+              }
             }
             }
           }
           }
         }
         }