使用WebClient或HttpClient下载文件?



我正在尝试从URL下载文件,我必须在WebClient和HttpClient之间进行选择。我已经参考了这篇文章和互联网上的其他几篇文章。在任何地方,都建议使用HttpClient,因为它具有出色的异步支持和其他.Net 4.5特权。但我仍然不完全相信,需要更多的投入。

我正在使用以下代码从互联网下载文件:

网络客户端:

WebClient client = new WebClient();
client.DownloadFile(downloadUrl, filePath);

HttpClient:

using (HttpClient client = new HttpClient())
{        
using (HttpResponseMessage response = await client.GetAsync(url))
using (Stream streamToReadFrom = await response.Content.ReadAsStreamAsync())
{
}
}

从我的角度来看,我只能看到使用 WebClient 的一个缺点,那就是非异步调用,阻塞调用线程。但是,如果我不担心线程阻塞或使用client.DownloadFileAsync()来利用异步支持怎么办?

另一方面,如果我使用 HttpClient,我不是将文件的每个字节加载到内存中,然后将其写入本地文件吗?如果文件大小太大,内存开销会不会很昂贵?如果我们使用 WebClient,这是可以避免的,因为它将直接写入本地文件而不消耗系统内存。

那么,如果性能是我的首要任务,我应该使用哪种方法进行下载?如果我的上述假设是错误的,我想澄清一下,我也愿意接受其他方法。

您可以使用 .Net 4.5+ 以本机方式执行此操作。 我尝试按照你的方式做,然后我只是在智能感知中找到了一种似乎有意义的方法。

https://learn.microsoft.com/en-us/dotnet/api/system.io.stream.copytoasync?view=netframework-4.7.2

uri = new Uri(generatePdfsRetrieveUrl + pdfGuid + ".pdf");
var response = await httpClient.GetAsync(uri);
using (var fs = new FileStream(
HostingEnvironment.MapPath(string.Format("~/Downloads/{0}.pdf", pdfGuid)), 
FileMode.CreateNew))
{
await response.Content.CopyToAsync(fs);
}

这是我的方法。

如果要调用 WebApi 来获取文件,则可以从控制器方法使用 HttpClient GET 请求并使用 FileStreamResult 返回类型返回文件流。

public async Task<ActionResult> GetAttachment(int FileID)
{
UriBuilder uriBuilder = new UriBuilder();
uriBuilder.Scheme = "https";
uriBuilder.Host = "api.example.com";
var Path = "/files/download";
uriBuilder.Path = Path;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(uriBuilder.ToString());
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("authorization", access_token); //if any
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(uriBuilder.ToString());
if (response.IsSuccessStatusCode)
{
System.Net.Http.HttpContent content = response.Content;
var contentStream = await content.ReadAsStreamAsync(); // get the actual content stream
return File(contentStream, content_type, filename);
}
else
{
throw new FileNotFoundException();
}
}
}

为了在我使用 WebClient 的现有代码上使用 HttpClient,我编写了一个小的扩展方法,以与我在代码上使用DownloadFileTaskAsync相同的方式使用它。

using (var client = new System.Net.Http.HttpClient()) // WebClient
{
var fileName = @"C:tempimgd.jpg";
var uri = new Uri("https://yourwebsite.com/assets/banners/Default.jpg");
await client.DownloadFileTaskAsync(uri, fileName);
}

要使用它,我们可以有这个扩展方法:

public static class HttpClientUtils
{
public static async Task DownloadFileTaskAsync(this HttpClient client, Uri uri, string FileName)
{
using (var s = await client.GetStreamAsync(uri))
{
using (var fs = new FileStream(FileName, FileMode.CreateNew))
{
await s.CopyToAsync(fs);
}
}
}
}

对于重复调用的代码,您不希望HttpClient放在using块中(这将使挂起的端口保持打开状态(

为了使用HttpClient下载文件,我发现这种扩展方法对我来说似乎是一个很好的可靠的解决方案:

public static class HttpContentExtensions
{
public static Task ReadAsFileAsync(this HttpContent content, string filename, bool overwrite)
{
string pathname = Path.GetFullPath(filename);
if (!overwrite && File.Exists(filename))
{
throw new InvalidOperationException(string.Format("File {0} already exists.", pathname));
}
FileStream fileStream = null;
try
{
fileStream = new FileStream(pathname, FileMode.Create, FileAccess.Write, FileShare.None);
return content.CopyToAsync(fileStream).ContinueWith(
(copyTask) =>
{
fileStream.Close();
});
}
catch
{
if (fileStream != null)
{
fileStream.Close();
}
throw;
}
}
}

以下是使用它下载URL并将其保存到文件的一种方法: (我使用的是Windows 7,因此没有WindowsRT可供我使用,所以我也在使用 System.IO。

public static class WebUtils
{
private static Lazy<IWebProxy> proxy = new Lazy<IWebProxy>(() => string.IsNullOrEmpty(Settings.Default.WebProxyAddress) ? null : new WebProxy { Address = new Uri(Settings.Default.WebProxyAddress), UseDefaultCredentials = true });
public static IWebProxy Proxy
{
get { return WebUtils.proxy.Value; }
}
public static Task DownloadAsync(string requestUri, string filename)
{
if (requestUri == null)
throw new ArgumentNullException(“requestUri”);
return DownloadAsync(new Uri(requestUri), filename);
}
public static async Task DownloadAsync(Uri requestUri, string filename)
{
if (filename == null)
throw new ArgumentNullException("filename");
if (Proxy != null)
WebRequest.DefaultWebProxy = Proxy;
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri))
{
using (Stream contentStream = await (await httpClient.SendAsync(request)).Content.ReadAsStreamAsync(), stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, Constants.LargeBufferSize, true))
{
await contentStream.CopyToAsync(stream);
}
}
}
}
} 

请注意,代码在设置中保存我使用的代理服务器的地址(在工作中(,并在指定了此类设置时使用该地址。否则,它应该告诉您有关使用 HttpClient 测试版下载和保存文件的所有信息。

如果你想(或必须(同步执行此操作,但使用漂亮的HttpClient类,那么有这个简单的方法:

string requestString = @"https://example.com/path/file.pdf";
var GetTask = httpClient.GetAsync(requestString);
GetTask.Wait(WebCommsTimeout); // WebCommsTimeout is in milliseconds
if (!GetTask.Result.IsSuccessStatusCode)
{
// write an error
return;
}

using (var fs = new FileStream(@"c:pathfile.pdf", FileMode.CreateNew))
{
var ResponseTask = GetTask.Result.Content.CopyToAsync(fs);
ResponseTask.Wait(WebCommsTimeout);
}

我的方法很简单。使用FileStream您可以将其存储在本地文件夹中,或使用FileStreamResult从 API 返回它。 存储到本地文件夹的示例:

private async Task SaveDataIntoLocalFolder(string url,string fileName)
{
using (var client = new HttpClient())
{
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var stream = await response.Content.ReadAsStreamAsync();
var fileInfo = new FileInfo(fileName);
using (var fileStream = fileInfo.OpenWrite())
{
await stream.CopyToAsync(fileStream);
}
}
else
{
throw new Exception("File not found");
}
}     
}

这是一个用于下载图像文件的简单演示 UWP 应用程序。 只需粘贴图像URL链接,然后按下载按钮即可。您可以识别文件类型并更改fileName以下载所需的文件。

MainPage.xaml

<Page
x:Class="HttpDownloader.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:HttpDownloader"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<StackPanel>
<TextBox x:Name="uriInput"
Header="URI:" PlaceholderText="Please provide an uri"
Width="300"
HorizontalAlignment="Center"/>
<Button Content="Dowload"
HorizontalAlignment="Center"
Click="Button_Click"/>
</StackPanel>
</Grid>
</Page>

MainPage.xaml.xs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using System.Net.Http;
using System.Net;
using Windows.Storage.Streams;
using Windows.Storage.Pickers;
using Windows.Storage;
using Windows.Graphics.Imaging;
using System.Threading;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace HttpDownloader
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
HttpClient client = new HttpClient();
string imageUrl = uriInput.Text;
try
{
using (var cancellationTokenSource = new CancellationTokenSource(50000))
{
var uri = new Uri(WebUtility.HtmlDecode(imageUrl));
using (var response = await client.GetAsync(uri, cancellationTokenSource.Token))
{
response.EnsureSuccessStatusCode();
var mediaType = response.Content.Headers.ContentType.MediaType;
string fileName = DateTime.Now.ToString("yyyyMMddhhmmss");
if (mediaType.IndexOf("jpg", StringComparison.OrdinalIgnoreCase) >= 0
|| mediaType.IndexOf("jpeg", StringComparison.OrdinalIgnoreCase) >= 0)
{
fileName += ".jpg";
}
else if (mediaType.IndexOf("png", StringComparison.OrdinalIgnoreCase) >= 0)
{
fileName += ".png";
}
else if (mediaType.IndexOf("gif", StringComparison.OrdinalIgnoreCase) >= 0)
{
fileName += ".gif";
}
else if (mediaType.IndexOf("bmp", StringComparison.OrdinalIgnoreCase) >= 0)
{
fileName += ".bmp";
}
else
{
fileName += ".png";
}
// Get the app's local folder.
StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
// Create a new subfolder in the current folder.
// Replace the folder if already exists.
string desiredName = "Images";
StorageFolder newFolder = await localFolder.CreateFolderAsync(desiredName, CreationCollisionOption.ReplaceExisting);
StorageFile newFile = await newFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
using (Stream streamStream = await response.Content.ReadAsStreamAsync())
{
using (Stream streamToWriteTo = File.Open(newFile.Path, FileMode.Create))
{
await streamStream.CopyToAsync(streamToWriteTo);
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception occur");
Console.WriteLine(ex.ToString());
}
}
}
}

您将在此文件夹中找到该图像。

Users/[current user name]/AppData/Local/Packages/[Application package name]/LocalState/Images

HttpClient _client=new HttpClient();
byte[] buffer = null;
try
{       
HttpResponseMessage task = await _client.GetAsync("https://**FILE_URL**");
Stream task2 = await task.Content.ReadAsStreamAsync();
using (MemoryStream ms = new MemoryStream())
{
await task2.CopyToAsync(ms);
buffer = ms.ToArray();
}
File.WriteAllBytes("C:/**PATH_TO_SAVE**", buffer);  
}
catch
{
}

相关内容

  • 没有找到相关文章

最新更新