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

批量检测无效图书封面时XHR请求返回Blob为null的问题求助

问题分析与优化方案

核心问题根源

你遇到的Blob返回null和无效封面标记失效问题,主要来自两个关键原因:

  1. 闭包陷阱:循环里用var声明的imgInt是函数级作用域,当异步onload回调执行时,循环已经跑完,imgInt会固定指向数组最后一个元素的索引,导致判断逻辑完全混乱。
  2. 并发请求过载:一次性发起大量XHR请求,浏览器会限制并发数(通常Chrome是6个左右),超出的请求会被阻塞甚至直接失败,最终返回null的Blob。

另外,代码里没有处理请求失败的情况,一旦请求出错也会导致blob为null,触发控制台报错。


优化方案分步实现

1. 基础修复:解决闭包陷阱与请求报错

把循环里的var换成let,利用块级作用域让每个循环迭代保留独立变量引用,同时增加错误处理:

/* 填入已知无效图书封面的文件大小、高度和宽度:子数组顺序为[字节数, 高度, 宽度] */
var noBookCoverSizes = [[2724,100,80],[2727,100,80],[154,120,80]];
jQuery(document).ready(function(){
    /* 确认当前页面为目标页面 */
    if (jQuery('.portlet-queryRecordSearchResult').length && !jQuery('.portlet-catalogueDetail').length) {
        /* 遍历每个图书封面 */
        jQuery('.arena-record-cover a img').each(function(){
            const img = jQuery(this);
            const imgUrl = img.attr('src');
            const imgHeight = img[0].naturalHeight;
            const imgWidth = img[0].naturalWidth;
            
            /* 遍历无效封面参数数组,用let创建块级作用域 */
            for (let i = 0; i < noBookCoverSizes.length; i++) {
                /* 检查宽高是否匹配 */
                if(noBookCoverSizes[i][1] === imgHeight && noBookCoverSizes[i][2] === imgWidth) {
                    const targetSize = noBookCoverSizes[i][0];
                    const xhr = new XMLHttpRequest();
                    /* 发起GET请求 */
                    xhr.open('GET', imgUrl, true);
                    xhr.responseType = 'blob';
                    
                    // 增加请求失败处理
                    xhr.onerror = function() {
                        console.warn(`请求封面失败: ${imgUrl}`);
                    };
                    
                    xhr.onload = function() {
                        const blob = this.response;
                        /* 先判断blob存在,再检查文件大小 */
                        if(blob && blob.size === targetSize) {
                            /* 用closest替代多层parent,结构变化也不会失效 */
                            img.closest('.arena-record-cover').addClass('no-book-cover');
                        }
                    }
                    xhr.send();
                }
            }
        });
    }
});

2. 进阶优化:控制并发请求数量(彻底解决过载问题)

如果页面封面数量较多,还是会触发浏览器并发限制,我们可以用请求队列来控制同时发起的请求数:

/* 填入已知无效图书封面的文件大小、高度和宽度:子数组顺序为[字节数, 高度, 宽度] */
const noBookCoverSizes = [[2724,100,80],[2727,100,80],[154,120,80]];
// 并发请求上限,建议设为5-6,匹配浏览器默认限制
const MAX_CONCURRENT = 5;
// 请求队列与活跃请求计数
let requestQueue = [];
let activeRequests = 0;

// 封装检查无效封面的异步函数
function checkInvalidCover(img, imgUrl, imgHeight, imgWidth) {
    return new Promise((resolve) => {
        // 先过滤匹配宽高的无效尺寸
        const matchedSizes = noBookCoverSizes.filter(size => size[1] === imgHeight && size[2] === imgWidth);
        if (matchedSizes.length === 0) {
            resolve(false);
            return;
        }
        
        const xhr = new XMLHttpRequest();
        xhr.open('GET', imgUrl, true);
        xhr.responseType = 'blob';
        
        xhr.onload = function() {
            const blob = this.response;
            const isInvalid = blob && matchedSizes.some(size => size[0] === blob.size);
            if (isInvalid) {
                img.closest('.arena-record-cover').addClass('no-book-cover');
            }
            resolve(isInvalid);
        };
        
        xhr.onerror = function() {
            console.warn(`请求封面失败: ${imgUrl}`);
            resolve(false);
        };
        
        xhr.send();
    });
}

// 处理请求队列的函数
function processQueue() {
    while (activeRequests < MAX_CONCURRENT && requestQueue.length > 0) {
        const task = requestQueue.shift();
        activeRequests++;
        task().then(() => {
            activeRequests--;
            processQueue();
        });
    }
}

jQuery(document).ready(function(){
    /* 确认当前页面为目标页面 */
    if (jQuery('.portlet-queryRecordSearchResult').length && !jQuery('.portlet-catalogueDetail').length) {
        /* 遍历每个图书封面,加入请求队列 */
        jQuery('.arena-record-cover a img').each(function(){
            const img = jQuery(this);
            const imgUrl = img.attr('src');
            const imgHeight = img[0].naturalHeight;
            const imgWidth = img[0].naturalWidth;
            
            requestQueue.push(() => checkInvalidCover(img, imgUrl, imgHeight, imgWidth));
        });
        
        // 启动队列处理
        processQueue();
    }
});

3. 额外优化:缓存请求结果(避免重复请求)

如果多个封面使用同一个无效图片URL,我们可以缓存请求结果,减少重复请求:

// 新增URL-文件大小缓存对象
const urlSizeCache = new Map();

// 修改checkInvalidCover函数,加入缓存逻辑
function checkInvalidCover(img, imgUrl, imgHeight, imgWidth) {
    return new Promise((resolve) => {
        const matchedSizes = noBookCoverSizes.filter(size => size[1] === imgHeight && size[2] === imgWidth);
        if (matchedSizes.length === 0) {
            resolve(false);
            return;
        }
        
        // 先检查缓存
        if (urlSizeCache.has(imgUrl)) {
            const size = urlSizeCache.get(imgUrl);
            const isInvalid = matchedSizes.some(item => item[0] === size);
            if (isInvalid) {
                img.closest('.arena-record-cover').addClass('no-book-cover');
            }
            resolve(isInvalid);
            return;
        }
        
        // 无缓存时发起请求
        const xhr = new XMLHttpRequest();
        xhr.open('GET', imgUrl, true);
        xhr.responseType = 'blob';
        
        xhr.onload = function() {
            const blob = this.response;
            const size = blob ? blob.size : -1;
            urlSizeCache.set(imgUrl, size); // 存入缓存
            const isInvalid = blob && matchedSizes.some(item => item[0] === size);
            if (isInvalid) {
                img.closest('.arena-record-cover').addClass('no-book-cover');
            }
            resolve(isInvalid);
        };
        
        xhr.onerror = function() {
            console.warn(`请求封面失败: ${imgUrl}`);
            urlSizeCache.set(imgUrl, -1); // 标记请求失败
            resolve(false);
        };
        
        xhr.send();
    });
}

测试验证

先测试基础修复版代码,应该能解决添加第三个无效尺寸后标记失效的问题;如果页面封面数量较多,再使用带并发控制的版本,就能彻底解决Blob返回null的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:37:44