问答 查看内容
返回列表

资源迁移失败,失败原因是页面上图片找不到,怎么解决

10 1
发表于 2 小时前 | 查看全部 阅读模式
截图202605061147475128.png

评论1

观小程楼主Lv.1 发表于 2 小时前 | 查看全部
问题原因:
页面上曾经使用了一些背景图片,后续又将这些背景图片删除了,但是在资源中并没有删除,因此在资源迁移过程中还会继续搜索这个资源
解决方法:
进入相关页面编辑页,打开F12,进入页面样式,在浏览器开发者模式下控制台输入相关指令,保存退出就可以了
截图202605061148375667.png 截图202605061150016904.png

代码内容:
GD.registerFetchMiddleware(
    '页面主题保存时验证并清理失效图片URL(优化版)',
    async ({ body, url, method, headers }) => {
        // 只拦截页面主题保存的请求
        if (!url.includes('/api/page/theme/') || method !== 'POST') {
            return { headers, body }
        }

        console.log('[图片URL验证] 开始处理请求:', url)

        // URL 验证结果缓存(避免重复验证相同的 URL)
        const urlValidationCache = new Map()

        /**
         * 验证图片 URL 是否有效
         * @param {string} imageUrl - 图片 URL
         * @returns {Promise<boolean>} - true 表示有效,false 表示失效
         */
        async function validateImageUrl(imageUrl) {
            if (!imageUrl || typeof imageUrl !== 'string') {
                return false
            }

            // 检查缓存
            if (urlValidationCache.has(imageUrl)) {
                return urlValidationCache.get(imageUrl)
            }

            try {
                // 构造完整的 URL(如果是相对路径)
                const fullUrl = imageUrl.startsWith('http')
                    ? imageUrl
                    : `${window.location.origin}${imageUrl}`

                const response = await fetch(fullUrl, {
                    method: 'HEAD', // 使用 HEAD 方法,只获取响应头,不下载文件内容
                    cache: 'no-cache',
                    signal: AbortSignal.timeout(5000) // 5秒超时
                })

                const isValid = response.ok // 状态码 200-299 表示有效

                // 缓存结果
                urlValidationCache.set(imageUrl, isValid)

                if (!isValid) {
                    console.log(`[图片URL验证] URL失效 (${response.status}):`, imageUrl)
                }
                return isValid
            } catch (error) {
                console.log('[图片URL验证] URL验证失败:', imageUrl, error.message)
                // 验证失败视为 URL 无效
                urlValidationCache.set(imageUrl, false)
                return false
            }
        }

        /**
         * 构建对象路径字符串
         * @param {Array} pathArray - 路径数组
         * @returns {string} - 路径字符串
         */
        function buildPathString(pathArray) {
            let result = ''
            for (let i = 0; i < pathArray.length; i++) {
                const segment = pathArray
                if (typeof segment === 'number') {
                    // 数组索引
                    result += `[${segment}]`
                } else if (segment.startsWith('[')) {
                    // 特殊标记(如 [key=xxx])
                    result += segment
                } else {
                    // 普通属性名
                    result += (i === 0 ? '' : '.') + segment
                }
            }
            return result
        }

        /**
         * 收集对象中所有需要验证的图片 URL
         * @param {any} obj - 要处理的对象
         * @param {Array} urlList - 收集到的 URL 列表
         * @param {Array} path - 当前路径
         */
        function collectImageUrls(obj, urlList = [], path = []) {
            if (!obj || typeof obj !== 'object') {
                return urlList
            }

            // 如果是数组,递归处理每个元素
            if (Array.isArray(obj)) {
                 for (let i = 0; i < obj.length; i++) {
                    const item = obj
                    // 检查是否有特殊的标识字段(如 key, id 等)
                    let indexLabel = i
                    if (item && typeof item === 'object') {
                        if (item.key) {
                            indexLabel = `[key=${item.key}]`
                        } else if (item.id) {
                            indexLabel = `[id=${item.id}]`
                        } else if (item.cdId) {
                            indexLabel = `[cdId=${item.cdId}]`
                        } else if (item.tabId) {
                            indexLabel = `[tabId=${item.tabId}]`
                        } else if (item.i) {
                            indexLabel = `[i=${item.i}]`
                        }
                    }
                    collectImageUrls(item, urlList, [...path, indexLabel])
                }
                return urlList
            }

            // 检查当前对象是否是图片对象格式
            const isImageObject =
                obj.hasOwnProperty('url') &&
                typeof obj.url === 'string' &&
                obj.hasOwnProperty('sourceType') &&
                obj.hasOwnProperty('renderType')

            if (isImageObject && obj.url) {
                urlList.push({
                    url: obj.url,
                    ref: obj, // 保存对象引用,方便后续删除 url 字段
                    path: buildPathString([...path, 'url']) // 保存完整路径
                })
            }

            // 递归处理对象的所有属性
            for (const key in obj) {
                if (obj.hasOwnProperty(key)) {
                    collectImageUrls(obj[key], urlList, [...path, key])
                }
            }

            return urlList
        }

        /**
         * 并发控制的 Promise 执行器
         * @param {Array} tasks - 任务数组
         * @param {number} concurrency - 并发数
         * @returns {Promise<Array>} - 结果数组
         */
        async function executeConcurrently(tasks, concurrency = 5) {
            const results = []
            const executing = []

            for (const [index, task] of tasks.entries()) {
                const promise = Promise.resolve().then(() => task()).then(result => {
                    results[index] = result
                })

                results.push(promise)
                executing.push(promise)

                if (executing.length >= concurrency) {
                    await Promise.race(executing)
                    executing.splice(executing.findIndex(p => p === promise), 1)
                }
            }

            await Promise.all(results)
            return results
        }

        try {
            // 深拷贝 body 以避免修改原对象
            const newBody = JSON.parse(JSON.stringify(body))

            // 收集所有需要验证的图片 URL
            const imageUrls = collectImageUrls(newBody)

            if (imageUrls.length === 0) {
                console.log('[图片URL验证] 未发现需要验证的图片URL')
                return { headers, body: newBody }
            }

            console.log(`[图片URL验证] 发现 ${imageUrls.length} 个图片URL,开始验证...`)

            // 创建验证任务
            const validationTasks = imageUrls.map(({ url }) => {
                return () => validateImageUrl(url)
            })

            // 并发执行验证(最多同时 5 个请求)
            const validationResults = await executeConcurrently(validationTasks, 5)

            // 删除失效的 URL 字段
            let removedCount = 0
            imageUrls.forEach(({ url, ref, path }, index) => {
                if (!validationResults[index]) {
                    console.log('[图片URL验证] 删除失效URL:', url)
                    console.log('[图片URL验证] 路径位置:', path)
                    delete ref.url
                    removedCount++
                }
            })

            console.log(`[图片URL验证] 处理完成,删除了 ${removedCount} 个失效URL`)
            return { headers, body: newBody }
        } catch (error) {
            console.error('[图片URL验证] 处理过程出错:', error)
            // 出错时返回原始 body
            return { headers, body }
        }
    },
    null,
    100 // 优先级
)

console.log('[图片URL验证] 中间件已注册(优化版)')

回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

微信服务号
联系我们
电话:400-880-0750
邮箱:hello@guandata.com
Copyright © 2001-2026 观远社区 版权所有 All Rights Reserved. 浙 ICP 备15006424号-3
去回复 去发帖 返回顶部
快速回复 返回顶部 返回列表