You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome扩展中epub.js、jszip.js等文件加载失败求助

问题

开发Chrome扩展用于读取解析上传的EPUB文件,因Content Security Policy(CSP)限制无法外链JS文件,已下载epub.js、jszip.min.js、jszip-utils.min.js并配置到web_accessible_resources中。但上传文件时,loadBook函数陷入循环,反复输出日志:Error: JSZip library not loaded, trying again。

上传文件执行的代码如下:

function handleFileSelection(event) {
   const file = event.target.files[0]; // 获取选中的文件
   const reader = new FileReader();

   reader.onload = function (event) {
   // 文件读取完成
   const fileData = event.target.result; // 获取文件数据

   console.log('fileData: ', fileData);

   // 创建EPUB.js的脚本元素
   const epubScript = document.createElement('script');
   epubScript.src = chrome.runtime.getURL('epub.js');
   document.head.appendChild(epubScript);

   // 创建JSZip的脚本元素
   const jszipScript = document.createElement('script');
   jszipScript.src = chrome.runtime.getURL('jszip.min.js');
   document.head.appendChild(jszipScript);

   // 创建JSZip Utils的脚本元素
   const jszipUtilsScript = document.createElement('script');
   jszipUtilsScript.src = chrome.runtime.getURL('jszip-utils.min.js');
   document.head.appendChild(jszipUtilsScript);

   // 等待所有脚本加载完成
   Promise.all([
     new Promise((resolve) => {
       epubScript.onload = resolve;
     }),
     new Promise((resolve) => {
       jszipScript.onload = resolve;
     }),
     new Promise((resolve) => {
       jszipUtilsScript.onload = resolve;
     }),
   ])
     .then(() => {
       // 所有脚本加载完成,开始使用
       console.log('EPUB.js and JSZip libraries are loaded');

       // 创建书籍实例
       const book = ePub();

       // 执行书籍逻辑
       loadBook();

       function loadBook() {
         if (typeof JSZip === 'undefined' || typeof JSZip.utils === 'undefined') {
           console.log('Error: JSZip library not loaded, trying again');
           setTimeout(loadBook, 1000);
           return;
         }

       }
     })
     .catch((error) => {
       console.error('Failed to load EPUB.js or JSZip:', error);
     });
 };

}

解决方案及优化方案

一、修复当前加载失败问题

  1. 调整脚本加载顺序
    EPUB.js依赖JSZip,必须保证JSZip先于EPUB.js加载。当前代码同时添加三个脚本到页面,执行顺序无法保证。调整加载顺序为:jszip.min.js → jszip-utils.min.js → epub.js,确保依赖关系正确:

    // 先加载JSZip
    const jszipScript = document.createElement('script');
    jszipScript.src = chrome.runtime.getURL('jszip.min.js');
    document.head.appendChild(jszipScript);
    
    // 再加载JSZip Utils
    const jszipUtilsScript = document.createElement('script');
    jszipUtilsScript.src = chrome.runtime.getURL('jszip-utils.min.js');
    document.head.appendChild(jszipUtilsScript);
    
    // 最后加载EPUB.js
    const epubScript = document.createElement('script');
    epubScript.src = chrome.runtime.getURL('epub.js');
    document.head.appendChild(epubScript);
    
  2. 验证web_accessible_resources配置
    在manifest.json中确认配置路径与实际项目结构一致,示例配置:

    "web_accessible_resources": [
      {
        "resources": ["epub.js", "jszip.min.js", "jszip-utils.min.js"],
        "matches": ["<all_urls>"]
      }
    ]
    

    若文件存放在子目录(如lib/),需修改路径为lib/epub.js这类格式。

  3. 移除冗余的loadBook循环
    既然已经通过Promise.all确保所有脚本加载完成,无需再检查JSZip是否存在,直接在then回调中执行后续逻辑:

    .then(() => {
      console.log('EPUB.js and JSZip libraries are loaded');
      const book = ePub();
      // 直接执行EPUB解析逻辑
      book.open(fileData).then(() => {
        // 处理书籍元数据、章节等内容
        console.log('书籍标题:', book.metadata.title);
      });
    })
    

二、更优方案:利用扩展背景页/服务工作者处理

直接在页面上下文加载第三方库易引发CSP问题和变量污染,推荐将解析逻辑移至扩展的隔离环境:

  1. 配置服务工作者背景页
    在manifest.json中声明:

    "background": {
      "service_worker": "background.js"
    }
    

    在background.js中通过importScripts加载依赖库,并监听消息:

    importScripts('jszip.min.js', 'jszip-utils.min.js', 'epub.js');
    
    chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
      if (message.type === 'parseEpub') {
        const book = ePub();
        book.open(message.fileData).then(() => {
          sendResponse({
            success: true,
            title: book.metadata.title,
            chapters: book.spine.items.map(item => item.href)
          });
        });
        return true; // 保持端口开放,异步返回结果
      }
    });
    
  2. 页面脚本发送消息调用解析逻辑

    reader.onload = function(event) {
      const fileData = event.target.result;
      chrome.runtime.sendMessage(
        {type: 'parseEpub', fileData},
        (response) => {
          if (response.success) {
            console.log('解析完成,书籍标题:', response.title);
          }
        }
      );
    };
    
  3. 避免重复加载脚本
    当前代码每次上传文件都会重新加载三个脚本,可通过标记变量避免重复加载:

    let librariesLoaded = false;
    let loadLibrariesPromise;
    
    function loadLibraries() {
      if (!loadLibrariesPromise) {
        loadLibrariesPromise = new Promise((resolve) => {
          // 脚本加载逻辑...
          Promise.all([/* 加载Promise数组 */]).then(resolve);
        });
      }
      return loadLibrariesPromise;
    }
    
    function handleFileSelection(event) {
      loadLibraries().then(() => {
        // 执行解析逻辑
      });
    }
    

内容的提问来源于stack exchange,提问作者SirBT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 11:37:13