可以直接在content.js里面写一个函数获取blob数据实现自定义下载吗?

2025-09-22 01:47:16 65 分享链接 开发笔记 浏览器扩展

可以的,你可以将获取 blob 数据的逻辑单独放在 content.js 中,通过消息通信的方式与 background.js 交互。这种方式更符合模块化设计,也便于维护。

具体实现思路是:

  1. content.js 中定义获取 blob 数据的函数,并监听来自后台的消息
  2. background.js 检测到 blob 下载时,向 content.js 发送消息请求获取 blob 数据
  3. content.js 处理请求并返回转换后的 dataURL 给后台
  4. 后台使用新文件名重新发起下载

background.js

// 监控所有下载事件
chrome.downloads.onCreated.addListener((downloadItem) => {
    console.log('检测到新下载:', downloadItem.url);

    // 只处理blob类型的下载
    if (!downloadItem.url.startsWith('blob:')) {
        return;
    }

    // 1. 取消原下载
    chrome.downloads.cancel(downloadItem.id, async () => {
        const cancelError = chrome.runtime.lastError;
        if (cancelError) {
            console.error('取消下载失败:', cancelError);
            return;
        }

        try {
            // 2. 生成新文件名(可自定义规则)
            const timestamp = new Date().getTime();
            // 从MIME类型推测扩展名(如image/png -> png)
            const ext = downloadItem.mime || 'dat';
            const newFileName = `custom_${timestamp}.${ext.split('/').pop()}`;

            // 3. 获取当前活跃标签页(blob所在页面)
            const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
            if (!tab) {
                console.error('未找到活跃标签页');
                return;
            }

            // 4. 向content.js发送消息,请求获取blob数据
            chrome.tabs.sendMessage(tab.id, {
                action: "fetchBlob",
                blobUrl: downloadItem.url
            }, (response) => {
                // 处理content.js的响应
                if (chrome.runtime.lastError) {
                    console.error('消息发送失败:', chrome.runtime.lastError);
                    return;
                }

                if (!response || !response.success) {
                    console.error('获取blob数据失败:', response?.error);
                    return;
                }

                // 5. 用新文件名重新下载
                chrome.downloads.download({
                    url: response.dataUrl,
                    filename: newFileName,
                    conflictAction: 'uniquify' // 文件名冲突时自动加后缀
                }, (downloadId) => {
                    const downloadError = chrome.runtime.lastError;
                    if (downloadError) {
                        console.error('重新下载失败:', downloadError);
                    } else {
                        console.log(`已重命名下载:${newFileName},ID:${downloadId}`);
                    }
                });
            });

        } catch (error) {
            console.error('处理blob下载时出错:', error);
        }
    });
});

content.js

// 在content script中定义获取blob数据的函数
async function getBlobData(blobUrl) {
    try {
        // 在网页上下文请求blob数据(此时blobUrl有效)
        const response = await fetch(blobUrl);
        const blob = await response.blob();

        // 转换为dataURL(可跨上下文传递)
        return new Promise((resolve) => {
            const fileReader = new FileReader();
            fileReader.onload = () => {
                resolve({
                    dataUrl: fileReader.result,
                    mimeType: blob.type,
                    success: true
                });
            };
            fileReader.onerror = () => {
                resolve({ success: false, error: "转换blob失败" });
            };
            fileReader.readAsDataURL(blob);
        });
    } catch (error) {
        console.error("content.js获取blob失败:", error);
        return { success: false, error: error.message };
    }
}

// 监听来自background的消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    // 只处理获取blob数据的请求
    if (message.action === "fetchBlob") {
        // 调用获取blob的函数并返回结果
        getBlobData(message.blobUrl).then(result => {
            sendResponse(result);
        });
        // 告诉Chrome要异步发送响应
        return true;
    }
});

manifest.json

{
    "manifest_version": 3,
    "name": "Blob下载重命名工具",
    "version": "1.0",
    "permissions": [
        "downloads",
        "scripting",
        "activeTab"
    ],
    "background": {
        "service_worker": "background.js"
    },
    "content_scripts": [
        {
            "matches": ["<all_urls>"], // 匹配所有网站
            "js": ["content.js"],     // 注入content script
            "run_at": "document_idle" // 页面空闲时注入
        }
    ]
}

代码说明

  1. 模块化分工

    • content.js:专注于在网页上下文处理 blob 数据(因为 blob URL 仅在网页上下文有效)
    • background.js:负责监控下载、协调流程、发起新下载
    • 两者通过 Chrome 的消息系统(chrome.runtime.sendMessage)通信
  2. 核心通信流程

    background检测到blob下载 → 取消原下载 → 向content发送获取blob请求 → 
    content在网页上下文获取并转换blob → 返回dataURL给background → 
    background用新文件名重新下载
  3. 优势

    • 代码分离,职责更清晰(content 处理网页相关操作,background 处理扩展核心逻辑)
    • 可复用性更高(getBlobData 函数可在其他场景调用)
    • 便于调试(content 脚本的日志会显示在网页的控制台,而非扩展背景页控制台)
  4. 注意事项

    • content.js 仅在 manifest.jsonmatches 配置的网站生效,这里用 <all_urls> 匹配所有网站
    • 消息传递是异步的,content.js 中需要用 return true 告诉 Chrome 要异步发送响应
    • 如果网页有严格的 CSP(内容安全策略),可能需要在 manifest.json 中配置 content_security_policy 兼容

这种方式既解决了 blob URL 跨上下文访问的问题,又保持了代码的模块化,是处理此类场景的推荐方案。

可以直接在content.js里面写一个函数获取blob数据实现自定义下载吗?