当不允许使用 HEAD 时验证图像是否存在于 URL
Verify an image exists at a URL when HEAD is not allowed
在 C# 中使用 HttpClient
,我试图在不下载实际图像的情况下验证给定 URL 中是否存在图像。这些图像可以来自任何 public 可访问的地址。我一直在使用似乎适用于 many/most 的 HEAD
HTTP 动词。 Google 事实证明驱动器映像很困难。
给定一个 public 分享 link 像这样:
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 请求,您可以在其中指定您只对 headers.
感兴趣
诀窍是将 HttpCompletionOption
enum 值传递给 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);
这是一个 in-depth article,详细说明了此枚举如何改变 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
在 C# 中使用 HttpClient
,我试图在不下载实际图像的情况下验证给定 URL 中是否存在图像。这些图像可以来自任何 public 可访问的地址。我一直在使用似乎适用于 many/most 的 HEAD
HTTP 动词。 Google 事实证明驱动器映像很困难。
给定一个 public 分享 link 像这样:
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 请求,您可以在其中指定您只对 headers.
诀窍是将 HttpCompletionOption
enum 值传递给 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);
这是一个 in-depth article,详细说明了此枚举如何改变 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