3

我编写了一些代码来从我的 Blogger 博客中导入内容。下载完所有 HTML 内容后,我会浏览图像标签并下载相应的图像。在很多情况下,System.Drawing.Bitmap.FromStream 会引发 ArgumentException。我正在下载的 URL 看起来不错,它提供了预期的图像(这是其中一个问题图像的 URL:http: //4.bp.blogspot.com/_tSWCyhtOc38/SgIPcctWRZI/AAAAAAAAAGg/2LLnVPxsogI/s1600- h/IMG_3590.jpg )。

    private static System.Drawing.Image DownloadImage(string source)
    {
        System.Drawing.Image image = null;

        // used to fetch content
        var client = new HttpClient();

        // used to store image data
        var memoryStream = new MemoryStream();

        try
        {
            // fetch the image
            var imageStream = client.GetStreamAsync(source).Result;

            // instantiate a system.drawing.image from the data
            image = System.Drawing.Bitmap.FromStream(imageStream, false, false);

            // save the image data to a memory stream
            image.Save(memoryStream, image.RawFormat);
        }
        catch (IOException exception)
        {
            Debug.WriteLine("{0} {1}", exception.Message, source);
        }
        catch (ArgumentException exception)
        {
            // sometimes, an image will link to a web page, resulting in this exception
            Debug.WriteLine("{0} {1}", exception.Message, source);
        }
        catch (AggregateException exception)
        {
            // sometimes, an image src will throw a 404
            Debug.WriteLine("{0} {1}", exception.Message, source);
        }
        finally
        {
            // clean up our disposable resources
            client.Dispose();
            memoryStream.Dispose();
        }

        return image;
    }

知道为什么在这里抛出 ArgumentException 吗?

编辑:我想到这可能是一个代理问题,所以我在我的 web.config 中添加了以下内容:

<system.net>
  <defaultProxy enabled="true" useDefaultCredentials="true">
    <proxy usesystemdefault="True" />
  </defaultProxy>
</system.net>

但是,添加该部分并没有任何区别。

编辑:此代码是从 EF 数据库初始化程序的上下文中调用的。这是一个堆栈跟踪:

Web.dll!Web.Models.Initializer.DownloadImage(string source) 第 234 行 C# Web.dll!Web.Models.Initializer.DownloadImagesForPost.AnonymousMethod__5(HtmlAgilityPack.HtmlNode tag) 第 126 行 + 0x8 字节 C# [外部代码] Web.dll !Web.Models.Initializer.DownloadImagesForPost(Web.Models.Post post) 第 119 行 + 0x34 字节 C# Web.dll!Web.Models.Initializer.Seed(Web.Models.FarmersMarketContext context) 第 320 行 + 0xb 字节 C# [外部代码] App_Web_l2h4tcej.dll!ASP._Page_Views_Home_Index_cshtml.Execute() 第 28 行 + 0x15 字节 C# [外部代码]

4

2 回答 2

4

好的,我发现了问题。事实证明,在某些情况下,Blogger 引用了呈现图像的 HTML 页面,而不是引用图像本身。因此,这种情况下的响应不是有效图像。在尝试保存图像数据之前,我添加了代码来检查响应标头,这解决了问题。为了其他遇到此问题的人的利益,这里是更新的代码:

    private static System.Drawing.Image DownloadImage(string source)
    {
        System.Drawing.Image image = null;

        // used to fetch content
        var client = new HttpClient();

        // used to store image data
        var memoryStream = new MemoryStream();

        try
        {
            // Blogger tacks on a -h to an image Url to link to an HTML page instead
            if (source.Contains("-h/"))
                source = source.Replace("-h/", "/");

            // fetch the image
            var response = client.GetAsync(source).Result;
            response.EnsureSuccessStatusCode();

            var contentType = response.Content.Headers.ContentType.MediaType;

            if (!contentType.StartsWith("image/"))
            {
                Debug.WriteLine(contentType);
                throw new ArgumentException("Specified source did not return an image");
            }

            var imageStream = response.Content.ReadAsStreamAsync().Result;

            // instantiate a system.drawing.image from the data
            image = System.Drawing.Bitmap.FromStream(imageStream, true, true);

            // save the image data to a memory stream
            image.Save(memoryStream, image.RawFormat);
        }
        catch (HttpRequestException exception)
        {
            // sometimes, we'll get a 404 or other unexpected response
            Debug.WriteLine("{0} {1}", exception.Message, source);
        }
        catch (IOException exception)
        {
            Debug.WriteLine("{0} {1}", exception.Message, source);
        }
        catch (ArgumentException exception)
        {
            // sometimes, an image will link to a web page, resulting in this exception
            Debug.WriteLine("{0} {1}", exception.Message, source);
        }
        finally
        {
            // clean up our disposable resources
            client.Dispose();
            memoryStream.Dispose();
        }

        return image;
    }
于 2012-07-10T15:15:56.867 回答
1

您正在处理另一个问题,我认为您是偶然解决的。不幸的是,GDI+ 异常不是很好,它们通常不能告诉您真正的问题是什么。

Image.FromStream() 实现中一个晦涩的花絮是 GDI+ 在从流中加载位图时使用流的 Seek() 方法。然而,这只有在流允许搜索时才能正常工作,它的 CanSeek 属性必须返回 true。这通常不是网络流的情况,没有提供足够的缓冲来允许任意搜索。

这是 HttpClient.GetStreamAsync() 的一个问题,它的 MSDN Library 文档说:

此方法不缓冲流

虽然您编写的工作版本使用 HttpContent.ReadAsStreamAsync(),但它的 MSDN 库文档说:

返回的 Task 对象将在所有内容写入字节数组后完成

So your first version doesn't work because the stream's CanSeek property is false, the second version works because the entire response is read into a byte array which permits seeking. The universal solution is to slurp the stream into a MemoryStream first.

于 2012-07-10T16:21:50.357 回答