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

Unity单例图片下载脚本的URL格式错误及多图下载异常问题

问题原因分析

1. 单例全局状态冲突

DownloadImage单例中的url、texture、isCached是全局共享变量,多个WebImage组件在Start方法中同时修改这些变量时会互相覆盖:

  • 比如第一个组件设置URL为A,第二个组件立刻将其改为B,最终单例只会使用最后一次设置的URL
  • 若某个组件的URL格式错误(为空或非法),会直接导致所有组件出现Malformed URL错误

2. 下载逻辑仅执行一次

单例的Start方法只会在初始化时调用一次GetImageFromWeb,后续修改url变量不会触发新的下载请求,完全无法响应WebImage组件的URL修改。

3. 缓存逻辑错误

  • 所有图片都硬编码保存为Image.jpg,新下载的图片会直接覆盖旧缓存,导致所有组件显示同一张图
  • Caching.ClearCache()是清除Unity内置的资源缓存,和自定义的本地文件缓存无关,逻辑完全错误

4. 错误处理逻辑混乱

GetImageFromWeb中将File.Exists和网络错误/HTTP错误放在同一个判断条件中,意味着只要本地存在缓存文件,不管网络请求是否成功,都会直接加载本地文件,完全违背了优先网络下载、缓存兜底的预期逻辑。

5. 性能与资源管理问题

WebImage的Update方法每帧调用GetComponent<RawImage>(),频繁的组件查找会消耗性能;且未判断texture是否更新就强制赋值,造成不必要的UI刷新。


修复方案

1. 重构DownloadImage单例

移除全局共享的url、texture、isCached变量,改为提供带回调的异步下载方法,支持多组件独立请求,同时修复缓存逻辑:

using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
using System.IO;
using System;

public class DownloadImage : MonoBehaviour
{
    public static DownloadImage instance;

    // 缓存目录,避免硬编码
    private string CacheDir => Path.Combine(Application.persistentDataPath, "ImageCache");

    private void Awake()
    {
        if (instance != null)
        {
            Destroy(gameObject);
            return;
        }
        instance = this;
        DontDestroyOnLoad(gameObject);
        // 确保缓存目录存在
        if (!Directory.Exists(CacheDir))
        {
            Directory.CreateDirectory(CacheDir);
        }
    }

    // 带回调的下载方法,每个WebImage独立调用
    public IEnumerator DownloadImageAsync(string targetUrl, bool useCache, Action<Texture2D> onComplete)
    {
        Texture2D resultTexture = null;
        string cachePath = GetCachePath(targetUrl);

        // 优先使用缓存(如果开启)
        if (useCache && File.Exists(cachePath))
        {
            byte[] cacheBytes = File.ReadAllBytes(cachePath);
            resultTexture = new Texture2D(2, 2);
            resultTexture.LoadImage(cacheBytes);
            onComplete?.Invoke(resultTexture);
            yield break;
        }

        // 网络下载
        using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(targetUrl))
        {
            yield return request.SendWebRequest();

            if (request.result == UnityWebRequest.Result.Success)
            {
                resultTexture = DownloadHandlerTexture.GetContent(request);
                onComplete?.Invoke(resultTexture);

                // 保存缓存(如果开启)
                if (useCache && resultTexture != null)
                {
                    byte[] textureBytes = resultTexture.EncodeToJPG();
                    File.WriteAllBytes(cachePath, textureBytes);
                }
            }
            else
            {
                Debug.LogError($"下载失败: {request.error}");
                onComplete?.Invoke(null);
            }
        }
    }

    // 根据URL生成唯一缓存文件名,避免覆盖
    private string GetCachePath(string url)
    {
        // 用URL的哈希值作为文件名,确保唯一性
        string fileName = $"{url.GetHashCode()}.jpg";
        return Path.Combine(CacheDir, fileName);
    }
}

2. 重构WebImage组件

缓存RawImage引用,独立调用单例的下载方法,避免修改单例全局状态:

using UnityEngine;
using UnityEngine.UI;
using System;

public class WebImage : MonoBehaviour
{
    [SerializeField] private string imageURL;
    [SerializeField] private bool cacheData;

    private RawImage _rawImage;

    private void Awake()
    {
        // 提前获取并缓存RawImage组件
        _rawImage = GetComponent<RawImage>();
        if (_rawImage == null)
        {
            _rawImage = gameObject.AddComponent<RawImage>();
        }
    }

    private void Start()
    {
        // 验证URL合法性
        if (!string.IsNullOrEmpty(imageURL) && Uri.IsWellFormedUriString(imageURL, UriKind.Absolute))
        {
            StartCoroutine(DownloadImage.instance.DownloadImageAsync(imageURL, cacheData, OnImageDownloaded));
        }
        else
        {
            Debug.LogError($"非法URL: {imageURL}");
        }
    }

    // 下载完成回调,更新自身显示
    private void OnImageDownloaded(Texture2D texture)
    {
        if (texture != null && _rawImage != null)
        {
            _rawImage.texture = texture;
        }
    }
}

关键修复点总结

  • 单例不再持有全局状态,改为提供独立的下载接口,支持多组件并行请求
  • 每个URL对应唯一的缓存文件,避免覆盖
  • 修复缓存逻辑:优先使用缓存(如果开启),网络下载成功后再保存缓存
  • 增加URL合法性校验,提前拦截非法URL导致的Malformed URL错误
  • 缓存组件引用,避免每帧查找,提升性能
  • 使用UnityWebRequest的新result属性替代已废弃的isNetworkError/isHttpError

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 12:01:05