在不允许 HEAD 时验证 URL 中是否存在图像

问题描述 投票:0回答:2

在 C# 中使用

HttpClient
,我尝试验证给定 URL 中是否存在图像,而不下载实际图像。这些图像可以来自任何可公开访问的地址。 我一直在使用
HEAD
HTTP 动词,它似乎对很多/大多数人都有效。 事实证明,谷歌驱动器图像很困难。

给定一个公共共享链接,如下所示:

https://drive.google.com/file/d/1oCmOEJp0vk73uYhzDTr2QJeKZOkyIm6v/view?usp=sharing
我可以愉快地使用
HEAD
,得到一个
200 OK
,它看起来很高兴。 但是,那不是图像。 这是一个可以下载图像的页面。

稍微修改一下,您可以将 URL 更改为实际获取图像,这正是我真正想要检查的:

https://drive.google.com/uc?export=download&id=1oCmOEJp0vk73uYhzDTr2QJeKZOkyIm6v

但是,使用

HEAD
点击该 URL 会产生
405 MethodNotAllowed
幸运的是,如果 URL 确实不存在,您会得到一个
404 NotFound

所以我被困在

405
。当不允许
HEAD
时,我的下一步是什么(不使用 Google API)?如果它根本不存在,我就不能假设它是一个有效的图像
404
。 我检查
Content-type
以验证它是一个图像,该图像存在超出本问题范围的问题。

c# google-drive-api dotnet-httpclient head
2个回答
3
投票

HttpClient
允许我们发出 http 请求,您可以在其中指定您仅对标头感兴趣。

技巧是将

HttpCompletionOption
enum 值传递给
SendAsync
或任何其他
{HttpVerb}Async
方法:

枚举名称 价值 描述
回复内容已读 0 读取包括内容在内的整个响应后,操作应完成。
响应标头读取 1 响应可用且标头被读取后,操作应立即完成。内容还没读完。
await client.GetAsync(targetUrlWhichDoesNotSupportHead, HttpCompletionOption.ResponseHeadersRead);

这是一篇深入的文章,详细介绍了此枚举如何改变

HttpClient
的行为和性能。

相关源码片段:


1
投票

太棒了,彼得! 谢谢你。

这是我的完整方法,供任何可能觉得有用的人使用:

public async Task<bool> ImageExists(string urlOrPath)
{
    try
    {
        var uri = new Uri(urlOrPath);
        if (uri.IsFile)
        {
            if (File.Exists(urlOrPath)) return true;
            _logger.LogError($"Cannot find image: [{urlOrPath}]");
            return false;
        }

        using (var result = await Get(uri))
        {
            if (result.StatusCode == HttpStatusCode.NotFound)
            {
                _logger.LogError($"Cannot find image: [{urlOrPath}]");
                return false;
            }
            if ((int)result.StatusCode >= 400)
            {
                _logger.LogError($"Error: {result.ReasonPhrase}. Image: [{urlOrPath}]");
                return false;
            }
            if (result.Content.Headers.ContentType == null)
            {
                _logger.LogError($"No 'ContentType' header returned.  Cannot validate image:[{urlOrPath}]");
                return false;
            }
            if(new[] { "image", "binary"}.All(v => !result.Content.Headers.ContentType.MediaType.SafeTrim().Contains(v)))
            {
                _logger.LogError($"'ContentType' {result.Content.Headers.ContentType.MediaType} is not an image. The Url may point to an HTML download page instead of an actual image:[{urlOrPath}]");
                return false;
            }
            var validTypes = new[] { "jpg", "jpeg", "gif", "png", "bmp", "binary" }; 
            if(validTypes.All(v => !result.Content.Headers.ContentType.MediaType.SafeTrim().Contains(v)))
            {
                _logger.LogError($"'ContentType' {result.Content.Headers.ContentType.MediaType} is not a valid image. Only [{string.Join(", ", validTypes)}] accepted. Image:[{urlOrPath}]");
                return false;
            }

            return true;
        }
    }
    catch (Exception e)
    {
        _logger.LogError($"There was a problem checking the image: [{urlOrPath}] is not valid. Error: {e.Message}");
        return false;
    }
}
private async Task<HttpResponseMessage> Get(Uri uri)
{
    var response = await _httpCli.SendAsync(new HttpRequestMessage(HttpMethod.Head, uri));
    if (response.StatusCode != HttpStatusCode.MethodNotAllowed) return response;

    return await _httpCli.SendAsync(new HttpRequestMessage() { RequestUri = uri }, HttpCompletionOption.ResponseHeadersRead);
}

编辑:添加了

Get()
方法,该方法仍然使用
HEAD
,并且仅在遇到
ResponseHeadersRead
时才使用
MethodNotAllowed
。使用现场场景我发现速度要快得多。不知道为什么。 YMMV

© www.soinside.com 2019 - 2024. All rights reserved.