It is not necessary to use System.Drawing
to find the image format in a URI. System.Drawing
is not available for .NET Core
unless you download the System.Drawing.Common NuGet package and therefore I don't see any good cross-platform answers to this question.
Also, my example does not use System.Net.WebClient
since Microsoft explicitly discourage the use of System.Net.WebClient
.
We don't recommend that you use the
WebClient
class for new development. Instead, use the System.Net.Http.HttpClient class.
*Without old System.Net.WebClient
and System.Drawing
.
This method will asynchronously download an image (or any file as long as the URI has a file extension) using the System.Net.Http.HttpClient
and then write it to a file, using the same file extension as the image had in the URI.
First part of getting the file extension is removing all the unnecessary parts from the URI.
We use Uri.GetLeftPart() with UriPartial.Path to get everything from the Scheme
up to the Path
.
In other words, https://www.example.com/image.png?query&with.dots
becomes https://www.example.com/image.png
.
After that, we use Path.GetExtension() to get only the extension (in my previous example, .png
).
var uriWithoutQuery = uri.GetLeftPart(UriPartial.Path);
var fileExtension = Path.GetExtension(uriWithoutQuery);
From here it should be straight forward. Download the image with HttpClient.GetByteArrayAsync, create the path, ensure the directory exists and then write the bytes to the path with File.WriteAllBytesAsync() (or File.WriteAllBytes
if you are on .NET Framework)
private async Task DownloadImageAsync(string directoryPath, string fileName, Uri uri)
{
using var httpClient = new HttpClient();
// Get the file extension
var uriWithoutQuery = uri.GetLeftPart(UriPartial.Path);
var fileExtension = Path.GetExtension(uriWithoutQuery);
// Create file path and ensure directory exists
var path = Path.Combine(directoryPath, $"{fileName}{fileExtension}");
Directory.CreateDirectory(directoryPath);
// Download the image and write to the file
var imageBytes = await _httpClient.GetByteArrayAsync(uri);
await File.WriteAllBytesAsync(path, imageBytes);
}
Note that you need the following using directives.
using System;
using System.IO;
using System.Threading.Tasks;
using System.Net.Http;
var folder = "images";
var fileName = "test";
var url = "https://cdn.discordapp.com/attachments/458291463663386646/592779619212460054/Screenshot_20190624-201411.jpg?query&with.dots";
await DownloadImageAsync(folder, fileName, new Uri(url));
HttpClient
for every method call. It is supposed to be reused throughout the application. I wrote a short example of an ImageDownloader
(50 lines) with more documentation that correctly reuses the HttpClient
and properly disposes of it that you can find here.