使用 HttpWebRequest 下载后 PNG 文件显示为红色问号

PNG file displayed as red question mark after download with HttpWebRequest

我面临以下问题:我从 google 驱动器下载一个 png 文件,所有字节都已成功接收(看起来),但是当我尝试构建 Texture2D 并设置它时as Sprite in an Image, 只出现红色的问号。

我是不是漏掉了什么?

代码如下:

HttpWebRequest request = (HttpWebRequest) WebRequest.Create("https://content.googleapis.com/drive/v3/files/" + fileId + "?alt=media");
request.Method = "GET";
request.ContentType = "image/png";
request.Headers.Add("Authorization", "Bearer " + AUTH_TOKEN);

WebResponse response = request.GetResponse();
byte[] bytes = new byte[response.ContentLength];
response.GetResponseStream().Read(bytes, 0, (int) response.ContentLength);
response.Close();

Texture2D texture2d = new Texture2D(8, 8);
Sprite sprite = null;
if (texture2d.LoadImage(bytes)) {
    sprite = Sprite.Create(texture2d, new Rect(0, 0, texture2d.width, texture2d.height), Vector2.zero);
}
if (sprite != null) {
    imageToUpdate.sprite = sprite;
}

您可能会收到错误响应或中间页面,这会使纹理无效。尝试通过两种方式检查接收到的数据是否真的是有效的PNG图片:

  1. 通过System.IO.File.WriteAllBytes(path, bytes)将其导出为图像文件。
  2. 前四个字节与the PNG header比较:

    //0x89, 'P', 'N', 'G'
    public static readonly byte[] PngHeaderBytes =
    {
        0x89, 0x50, 0x4E, 0x47
    };
    

另见 red question mark loading image as texture with WWW

我用你的确切代码和这个 image 做了一个快速测试,然后这样做:

问题是您没有下载所有图像字节。这不是如何使用 HttpWebRequest 下载所有图像字节。在 while 循环中使用 MemoryStreamStream.Read。如果没有别的可看的,Stream.Read会return0.

查看 downloadFullData 函数以了解如何正确执行此操作:

public Image imageToUpdate;

void Start()
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://wallpaper-gallery.net/images/hq-images-wallpapers/hq-images-wallpapers-12.jpg");
    request.Method = "GET";
    //request.ContentType = "image/png";
    //request.Headers.Add("Authorization", "Bearer " + AUTH_TOKEN);

    WebResponse response = request.GetResponse();

    if (response == null)
    {
        return;
    }

    //Download All the bytes
    byte[] bytes = downloadFullData(request);

    //Load Image
    Texture2D texture2d = new Texture2D(8, 8);
    Sprite sprite = null;
    if (texture2d.LoadImage(bytes))
    {
        sprite = Sprite.Create(texture2d, new Rect(0, 0, texture2d.width, texture2d.height), Vector2.zero);
    }
    if (sprite != null)
    {
        imageToUpdate.sprite = sprite;
    }
}

byte[] downloadFullData(HttpWebRequest request)
{
    using (WebResponse response = request.GetResponse())
    {

        if (response == null)
        {
            return null;
        }

        using (Stream input = response.GetResponseStream())
        {
            byte[] buffer = new byte[16 * 1024];
            using (MemoryStream ms = new MemoryStream())
            {
                int read;
                while (input.CanRead && (read = input.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
                return ms.ToArray();
            }
        }
    }
}

结果:

这应该可以,但不要使用 HttpWebRequest,因为它会阻止您的程序,直到图像下载完毕。如果你想使用它,那么你必须在另一个 Thread 中使用它。这从这里变得非常复杂,因为你不能在另一个 Thread 中使用 Unity 的 API 并且必须使用类似 this 的东西来调用 Unity 的 API( texture2d.LoadImage(bytes) ) 在另一个线程中。

为此使用 Unity 的 WWW or UnityWebRequest API。下面的示例将完成与 UnityWebRequest.

完全相同的事情
public Image imageToUpdate;

void Start()
{
    StartCoroutine(downloadImage());
}

IEnumerator downloadImage()
{
    string authorization = authenticate("YourUserName", "YourPass");

    string url = "http://wallpaper-gallery.net/images/hq-images-wallpapers/hq-images-wallpapers-12.jpg";

    UnityWebRequest www = UnityWebRequest.Get(url);
    //www.SetRequestHeader("AUTHORIZATION", authorization);

    DownloadHandler handle = www.downloadHandler;

    //Send Request and wait
    yield return www.Send();

    if (www.isError)
    {

        UnityEngine.Debug.Log("Error while Receiving: " + www.error);
    }
    else
    {
        UnityEngine.Debug.Log("Success");

        //Load Image
        Texture2D texture2d = new Texture2D(8, 8);
        Sprite sprite = null;
        if (texture2d.LoadImage(handle.data))
        {
            sprite = Sprite.Create(texture2d, new Rect(0, 0, texture2d.width, texture2d.height), Vector2.zero);
        }
        if (sprite != null)
        {
            imageToUpdate.sprite = sprite;
        }
    }
}

string authenticate(string username, string password)
{
    string auth = username + ":" + password;
    auth = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(auth));
    auth = "Basic " + auth;
    return auth;
}

我正在创建自定义 .NET 5 Web API 并从 Unity 中获取大量数据。我认为简单地 return 从我的图像中读取一个字节 [] 并解析它们就可以了,但是当我将 API 方法的 return 类型改回 FileResult 时,我的图像终于正确加载了.我的猜测是 byte[] 本身没有 Unity 需要的必要图像元数据,return将它们作为 FileResult 解决了这些格式问题。