如果不允许HEAD,请验证URL中是否存在图像



使用C#中的HttpClient,我试图在不下载实际图像的情况下验证给定URL中是否存在图像。这些图片可以来自任何可公开访问的地址。我一直在使用HEADHTTP谓词,它似乎适用于许多/大多数情况。事实证明,谷歌硬盘图像很难。

给定这样的公共共享链接: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以验证它是一个图像,它存在超出此问题范围的问题。

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

诀窍是将HttpCompletionOption枚举值传递给SendAsync或任何其他{HttpVerb}Async方法:

| Enum name           | Value | Description                                                                                                         |
|---------------------|-------|---------------------------------------------------------------------------------------------------------------------|
| ResponseContentRead | 0     | The operation should complete after reading the entire response including the content.                              |
| ResponseHeadersRead | 1     | The operation should complete as soon as a response is available and headers are read. The content is not read yet. |
await client.GetAsync(targetUrlWhichDoesNotSupportHead, HttpCompletionOption.ResponseHeadersRead);

下面是一篇深入的文章,详细介绍了此枚举如何更改HttpClient的行为和性能。

相关源代码片段:

  • 在.NET Framework的情况下
  • 在.NET Core的情况下

太棒了,彼得!非常感谢。

以下是我为任何可能觉得有用的人提供的完整方法:

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,只有遇到MethodNotAllowed时才使用ResponseHeadersRead。使用现场场景,我发现它要快得多。不知道为什么。YMMV

最新更新