I need to use "HTTP Post" with WebClient to post some data to a specific URL I have.
Now, I know this can be accomplished with WebRequest but for some reasons I want to use WebClient instead. Is that possible? If so, can someone show me some example or point me to the right direction?
I just found the solution and yea it was easier than I thought :)
so here is the solution:
string URI = "http://www.myurl.com/post.php";
string myParameters = "param1=value1¶m2=value2¶m3=value3";
using (WebClient wc = new WebClient())
{
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
string HtmlResult = wc.UploadString(URI, myParameters);
}
it works like charm :)
There is a built in method called UploadValues that can send HTTP POST (or any kind of HTTP methods) AND handles the construction of request body (concatenating parameters with "&" and escaping characters by url encoding) in proper form data format:
using(WebClient client = new WebClient())
{
var reqparm = new System.Collections.Specialized.NameValueCollection();
reqparm.Add("param1", "<any> kinds & of = ? strings");
reqparm.Add("param2", "escaping is already handled");
byte[] responsebytes = client.UploadValues("http://localhost", "POST", reqparm);
string responsebody = Encoding.UTF8.GetString(responsebytes);
}
Using WebClient.UploadString or WebClient.UploadData you can POST data to the server easily. I’ll show an example using UploadData, since UploadString is used in the same manner as DownloadString.
byte[] bret = client.UploadData("http://www.website.com/post.php", "POST",
System.Text.Encoding.ASCII.GetBytes("field1=value1&field2=value2") );
string sret = System.Text.Encoding.ASCII.GetString(bret);
More: http://www.daveamenta.com/2008-05/c-webclient-usage/
string URI = "site.com/mail.php";
using (WebClient client = new WebClient())
{
System.Collections.Specialized.NameValueCollection postData =
new System.Collections.Specialized.NameValueCollection()
{
{ "to", emailTo },
{ "subject", currentSubject },
{ "body", currentBody }
};
string pagesource = Encoding.UTF8.GetString(client.UploadValues(URI, postData));
}
//Making a POST request using WebClient.
Function()
{
WebClient wc = new WebClient();
var URI = new Uri("http://your_uri_goes_here");
//If any encoding is needed.
wc.Headers["Content-Type"] = "application/x-www-form-urlencoded";
//Or any other encoding type.
//If any key needed
wc.Headers["KEY"] = "Your_Key_Goes_Here";
wc.UploadStringCompleted +=
new UploadStringCompletedEventHandler(wc_UploadStringCompleted);
wc.UploadStringAsync(URI,"POST","Data_To_Be_sent");
}
void wc__UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
try
{
MessageBox.Show(e.Result);
//e.result fetches you the response against your POST request.
}
catch(Exception exc)
{
MessageBox.Show(exc.ToString());
}
}
Using simple client.UploadString(adress, content); normally works fine but I think it should be remembered that a WebException will be thrown if not a HTTP successful status code is returned. I usually handle it like this to print any exception message the remote server is returning:
try
{
postResult = client.UploadString(address, content);
}
catch (WebException ex)
{
String responseFromServer = ex.Message.ToString() + " ";
if (ex.Response != null)
{
using (WebResponse response = ex.Response)
{
Stream dataRs = response.GetResponseStream();
using (StreamReader reader = new StreamReader(dataRs))
{
responseFromServer += reader.ReadToEnd();
_log.Error("Server Response: " + responseFromServer);
}
}
}
throw;
}
Using webapiclient with model send serialize json parameter request.
PostModel.cs
public string Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int Age { get; set; }
WebApiClient.cs
internal class WebApiClient : IDisposable
{
private bool _isDispose;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
if (!_isDispose)
{
if (disposing)
{
}
}
_isDispose = true;
}
private void SetHeaderParameters(WebClient client)
{
client.Headers.Clear();
client.Headers.Add("Content-Type", "application/json");
client.Encoding = Encoding.UTF8;
}
public async Task<T> PostJsonWithModelAsync<T>(string address, string data,)
{
using (var client = new WebClient())
{
SetHeaderParameters(client);
string result = await client.UploadStringTaskAsync(address, data); // method:
//The HTTP method used to send the file to the resource. If null, the default is POST
return JsonConvert.DeserializeObject<T>(result);
}
}
}
Business caller method
public async Task<ResultDTO> GetResultAsync(PostModel model)
{
try
{
using (var client = new WebApiClient())
{
var serializeModel= JsonConvert.SerializeObject(model);// using Newtonsoft.Json;
var response = await client.PostJsonWithModelAsync<ResultDTO>("http://www.website.com/api/create", serializeModel);
return response;
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
Most of the answers are old. Just wanted to share what worked for me. In the interest of doing things asynchronously i.e. to post data to specific URL using WebClient asynchronously in .NET 6.0 Preview 7, .NET Core and other versions can be done using WebClient.UploadStringTaskAsync Method.
Use namespace System.Net; and for a class ResponseType to capture the response from the server, we can use this method to POST data to a specific URL. Make sure to use the await keyword while calling this method
public async Task<ResponseType> MyAsyncServiceCall()
{
try
{
var uri = new Uri("http://your_uri");
var body= "param1=value1¶m2=value2¶m3=value3";
using (var wc = new WebClient())
{
wc.Headers[HttpRequestHeader.Authorization] = "yourKey"; // Can be Bearer token, API Key etc.....
wc.Headers[HttpRequestHeader.ContentType] = "application/json"; // Is about the payload/content of the current request or response. Do not use it if the request doesn't have a payload/ body.
wc.Headers[HttpRequestHeader.Accept] = "application/json"; // Tells the server the kind of response the client will accept.
wc.Headers[HttpRequestHeader.UserAgent] = "PostmanRuntime/7.28.3";
string result = await wc.UploadStringTaskAsync(uri, body);
return JsonConvert.DeserializeObject<ResponseType>(result);
}
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
Here is the crisp answer:
public String sendSMS(String phone, String token) {
WebClient webClient = WebClient.create(smsServiceUrl);
SMSRequest smsRequest = new SMSRequest();
smsRequest.setMessage(token);
smsRequest.setPhoneNo(phone);
smsRequest.setTokenId(smsServiceTokenId);
Mono<String> response = webClient.post()
.uri(smsServiceEndpoint)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.body(Mono.just(smsRequest), SMSRequest.class)
.retrieve().bodyToMono(String.class);
String deliveryResponse = response.block();
if (deliveryResponse.equalsIgnoreCase("success")) {
return deliveryResponse;
}
return null;
}
Related
I have a code to make HTTP web request and get the response.How to unit test invoke method and I found some links related to unit test HTTP web request and response but they are confusing for me.
I am looking to test response with out running any server.
Unit test code:
[TestMethod]
public void TestMethod()
{
string request = "request content";
WebServiceClass webServiceClass = new WebServiceClass();
string actual_response;
webServiceClass.InvokeService
(request,"POST","application/json","/invoke",out actual_response);
string expected_response="response content";
Assert.AreEqual(actual_response, expected_response);
}
Actual code:
class WebServiceClass {
private HttpWebRequest BuildHttpRequest(String requestMethod, String contentType,String uriExtension, int dataBytes)
{
String composeUrl = webServiceURL + "/" + uriExtension;
Uri srvUri = new Uri(composeUrl);
HttpWebRequest httprequest = (HttpWebRequest)HttpWebRequest.Create(srvUri);
httprequest.Method = requestMethod;
httprequest.ContentType = contentType;
httprequest.KeepAlive = false;
httprequest.Timeout = webSrvTimeOut;
httprequest.ContentLength = dataBytes;
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
httprequest.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
return httprequest;
}
public void InvokeService(String request, String requestMethod, String contentType,String uriExtension, out String response)
{
Encoding encoding = new UTF8Encoding();
String serviceResponse = String.Empty;
byte[] dataBytes = encoding.GetBytes(request);
try
{
int dataLength = dataBytes.Length;
HttpWebRequest httprequest = BuildHttpRequest(requestMethod, contentType, uriExtension, dataLength);
using (StreamWriter writes = new StreamWriter(httprequest.GetRequestStream()))
{
writes.Write(request);
}
using (HttpWebResponse httpresponse = (HttpWebResponse)httprequest.GetResponse())
{
String httpstatuscode = httpresponse.StatusCode.ToString();
using (StreamReader reader = new StreamReader(httpresponse.GetResponseStream(), Encoding.UTF8))
{
serviceResponse = reader.ReadToEnd().ToString();
}
}
}
}
Maybe late, but I think this is what you want.
//Copy Code and paste in C# Interactive Window
//(Existed in VS 2015+, "View->Other Window->C# Interactive")
//If you writen an http request and need a response test
//but the target API hasn't complated
//you can use this code to built a simple responder in C# Interactive window
//without to create a new webapi project for testing!
using System.Net;
var l = new HttpListener();
l.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
l.Prefixes.Add("http://localhost:8080/API/XXXX/");//ATTACTION: Uri should end with "/"
//more target url
var t = Task.Run(() =>
{
Print("START!");
l.Start();
try
{
while (l.IsListening)
{
HttpListenerContext ctx = l.GetContext();//here for waiting
Print(new StreamReader(ctx.Request.InputStream).ReadToEnd());
////Set Response here
//ctx.Response.StatusCode = 200;
//ctx.Response.OutputStream.Write("true".Select(c => (byte)c).ToArray(), 0, 4);
ctx.Response.Close();//"Close" for return response
}
}
catch { Print("STOP!"); }
});
//l.Stop(); //Call this method to stop HttpListener
Welcome to star/fork my repository
https://github.com/Flithor/CSharpInteractive_Mini_HttpRequest_Responder
What is a simple way of downloading a file from a URL path?
using (var client = new WebClient())
{
client.DownloadFile("http://example.com/file/song/a.mpeg", "a.mpeg");
}
Include this namespace
using System.Net;
Download Asynchronously and put a ProgressBar to show the status of the download within the UI Thread Itself
private void BtnDownload_Click(object sender, RoutedEventArgs e)
{
using (WebClient wc = new WebClient())
{
wc.DownloadProgressChanged += wc_DownloadProgressChanged;
wc.DownloadFileAsync (
// Param1 = Link of file
new System.Uri("http://www.sayka.com/downloads/front_view.jpg"),
// Param2 = Path to save
"D:\\Images\\front_view.jpg"
);
}
}
// Event to track the progress
void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
Use System.Net.WebClient.DownloadFile:
string remoteUri = "http://www.contoso.com/library/homepage/images/";
string fileName = "ms-banner.gif", myStringWebResource = null;
// Create a new WebClient instance.
using (WebClient myWebClient = new WebClient())
{
myStringWebResource = remoteUri + fileName;
// Download the Web resource and save it into the current filesystem folder.
myWebClient.DownloadFile(myStringWebResource, fileName);
}
using System.Net;
WebClient webClient = new WebClient();
webClient.DownloadFile("http://mysite.com/myfile.txt", #"c:\myfile.txt");
WebRequest, WebClient, and ServicePoint are obsolete from .NET 6 (source - 11/2021).
Use the System.Net.Http.HttpClient class instead:
using (var client = new HttpClient())
{
using (var s = client.GetStreamAsync("https://via.placeholder.com/150"))
{
using (var fs = new FileStream("localfile.jpg", FileMode.OpenOrCreate))
{
s.Result.CopyTo(fs);
}
}
}
Async version of the same code:
using var client = new HttpClient();
using var s = await client.GetStreamAsync("https://via.placeholder.com/150");
using var fs = new FileStream("localfile.jpg", FileMode.OpenOrCreate);
await s.CopyToAsync(fs);
Complete class to download a file while printing status to console.
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Threading;
class FileDownloader
{
private readonly string _url;
private readonly string _fullPathWhereToSave;
private bool _result = false;
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(0);
public FileDownloader(string url, string fullPathWhereToSave)
{
if (string.IsNullOrEmpty(url)) throw new ArgumentNullException("url");
if (string.IsNullOrEmpty(fullPathWhereToSave)) throw new ArgumentNullException("fullPathWhereToSave");
this._url = url;
this._fullPathWhereToSave = fullPathWhereToSave;
}
public bool StartDownload(int timeout)
{
try
{
System.IO.Directory.CreateDirectory(Path.GetDirectoryName(_fullPathWhereToSave));
if (File.Exists(_fullPathWhereToSave))
{
File.Delete(_fullPathWhereToSave);
}
using (WebClient client = new WebClient())
{
var ur = new Uri(_url);
// client.Credentials = new NetworkCredential("username", "password");
client.DownloadProgressChanged += WebClientDownloadProgressChanged;
client.DownloadFileCompleted += WebClientDownloadCompleted;
Console.WriteLine(#"Downloading file:");
client.DownloadFileAsync(ur, _fullPathWhereToSave);
_semaphore.Wait(timeout);
return _result && File.Exists(_fullPathWhereToSave);
}
}
catch (Exception e)
{
Console.WriteLine("Was not able to download file!");
Console.Write(e);
return false;
}
finally
{
this._semaphore.Dispose();
}
}
private void WebClientDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
Console.Write("\r --> {0}%.", e.ProgressPercentage);
}
private void WebClientDownloadCompleted(object sender, AsyncCompletedEventArgs args)
{
_result = !args.Cancelled;
if (!_result)
{
Console.Write(args.Error.ToString());
}
Console.WriteLine(Environment.NewLine + "Download finished!");
_semaphore.Release();
}
public static bool DownloadFile(string url, string fullPathWhereToSave, int timeoutInMilliSec)
{
return new FileDownloader(url, fullPathWhereToSave).StartDownload(timeoutInMilliSec);
}
}
Usage:
static void Main(string[] args)
{
var success = FileDownloader.DownloadFile(fileUrl, fullPathWhereToSave, timeoutInMilliSec);
Console.WriteLine("Done - success: " + success);
Console.ReadLine();
}
Try using this:
private void downloadFile(string url)
{
string file = System.IO.Path.GetFileName(url);
WebClient cln = new WebClient();
cln.DownloadFile(url, file);
}
Also you can use DownloadFileAsync method in WebClient class. It downloads to a local file the resource with the specified URI. Also this method does not block the calling thread.
Sample:
webClient.DownloadFileAsync(new Uri("http://www.example.com/file/test.jpg"), "test.jpg");
For more information:
http://csharpexamples.com/download-files-synchronous-asynchronous-url-c/
Check for a network connection using GetIsNetworkAvailable() to avoid creating empty files when not connected to a network.
if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
{
using (System.Net.WebClient client = new System.Net.WebClient())
{
client.DownloadFileAsync(new Uri("http://www.examplesite.com/test.txt"),
"D:\\test.txt");
}
}
WebClient is obsolete
If you want to download to a file avoid first reading to memory by using ResponseHeadersRead like this:
static public async Task HttpDownloadFileAsync(HttpClient httpClient, string url, string fileToWriteTo) {
using HttpResponseMessage response = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
using Stream streamToReadFrom = await response.Content.ReadAsStreamAsync();
using Stream streamToWriteTo = File.Open(fileToWriteTo, FileMode.Create);
await streamToReadFrom.CopyToAsync(streamToWriteTo);
}
Above code is more of an outline, adding correct error/exception handling is not trivial, also progress report is not trivial, as is Disposing.
I came up with a set of C# 9.0 extension classes for DownoadFileAsync, GetToStringAsync and PostToStringAsync
namespace System.Net.Http {
// HttpResponse is in one of 3 states:
// - ResponseMessageInfo is object && ResponseMessageInfo.IsSuccessStatusCode -> success, inspect ResponseMessageInfo for StatusCode etc
// - ResponseMessageInfo is object && !ResponseMessageInfo.IsSuccessStatusCode -> failure, inspect ResponseMessageInfo for StatusCode, ReasonPhrase etc
// - ResponseMessageInfo is null -> exception, inspect ExceptionInfo fields
public record HttpResponse {
// copies of HttpRequestMessage and HttpResponseMessage which do not have the content and do not need to be disposed
public record HttpRequestMessageInfo(HttpRequestHeaders Headers, HttpMethod Method, HttpRequestOptions Options, Uri? RequestUri, Version Version, HttpVersionPolicy VersionPolicy);
public record HttpResponseMessageInfo(HttpResponseHeaders Headers, bool IsSuccessStatusCode, string? ReasonPhrase, HttpRequestMessageInfo RequestMessage, HttpStatusCode StatusCode, HttpResponseHeaders TrailingHeaders, Version Version);
// holds Http exception information
public record HttpExceptionInfo(HttpRequestMessageInfo HttpRequestMessage, string ErrorMessage, WebExceptionStatus? WebExceptionStatus);
// if ResponseMessageInfo is null ExceptionInfo is not and vice versa
public HttpResponseMessageInfo? ResponseMessageInfo { get; init; }
public HttpExceptionInfo? ExceptionInfo { get; init; }
public HttpResponse(HttpRequestMessage requestMessage, HttpResponseMessage responseMessage) {
var requestMessageInfo = new HttpRequestMessageInfo(requestMessage.Headers, requestMessage.Method, requestMessage.Options, requestMessage.RequestUri, requestMessage.Version, requestMessage.VersionPolicy);
ResponseMessageInfo = new(responseMessage.Headers, responseMessage.IsSuccessStatusCode, responseMessage.ReasonPhrase, requestMessageInfo, responseMessage.StatusCode, responseMessage.TrailingHeaders, responseMessage.Version);
ExceptionInfo = null;
}
public HttpResponse(HttpRequestMessage requestMessage, Exception exception) {
ResponseMessageInfo = null;
var requestMessageInfo = new HttpRequestMessageInfo(requestMessage.Headers, requestMessage.Method, requestMessage.Options, requestMessage.RequestUri, requestMessage.Version, requestMessage.VersionPolicy);
if (exception is WebException ex1 && ex1.Status == WebExceptionStatus.ProtocolError) {
using HttpWebResponse? httpResponse = (HttpWebResponse?)ex1.Response;
ExceptionInfo = new(requestMessageInfo, httpResponse?.StatusDescription ?? "", ex1.Status);
}
else if (exception is WebException ex2) ExceptionInfo = new(requestMessageInfo, ex2.FullMessage(), ex2.Status);
else if (exception is TaskCanceledException ex3 && ex3.InnerException is TimeoutException) ExceptionInfo = new(requestMessageInfo, ex3.InnerException.FullMessage(), WebExceptionStatus.Timeout);
else if (exception is TaskCanceledException ex4) ExceptionInfo = new(requestMessageInfo, ex4.FullMessage(), WebExceptionStatus.RequestCanceled);
else ExceptionInfo = new(requestMessageInfo, exception.FullMessage(), null);
}
public override string ToString() {
if (ResponseMessageInfo is object) {
var msg = ResponseMessageInfo.IsSuccessStatusCode ? "Success" : "Failure";
msg += $" {Enum.GetName(typeof(HttpStatusCode), ResponseMessageInfo.StatusCode)}";
if (ResponseMessageInfo.ReasonPhrase is object) msg += $" {ResponseMessageInfo.ReasonPhrase}";
return msg;
} else if (ExceptionInfo is object) {
var msg = "Failure";
msg += $" {ExceptionInfo.ErrorMessage}";
if (ExceptionInfo.WebExceptionStatus is object) msg += $" {Enum.GetName(typeof(WebExceptionStatus), ExceptionInfo.WebExceptionStatus)}";
return msg;
}
return "NA"; // never reach here
}
}
public static class ExtensionMethods {
// progressCallback recieves (bytesRecieved, percent, speedKbSec) and can return false to cancell download
public static async Task<(bool success, HttpResponse httpResponse)> DownloadFileAsync(this HttpClient httpClient, Uri requestUri, string fileToWriteTo, CancellationTokenSource? cts = null, Func<long, int, float, bool>? progressCallback = null) {
var httpRequestMessage = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = requestUri };
var created = false;
try {
var cancellationToken = cts?.Token ?? default;
using HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
if (!httpResponseMessage.IsSuccessStatusCode) return (false, new(httpRequestMessage, httpResponseMessage));
var contentLength = httpResponseMessage.Content.Headers.ContentLength;
using Stream streamToReadFrom = await httpResponseMessage.Content.ReadAsStreamAsync();
using Stream streamToWriteTo = File.Open(fileToWriteTo, FileMode.Create);
created = true;
var buffer = new byte[81920];
var bytesRecieved = (long)0;
var stopwatch = Stopwatch.StartNew();
int bytesInBuffer;
while ((bytesInBuffer = await streamToReadFrom.ReadAsync(buffer, cancellationToken)) != 0) {
await streamToWriteTo.WriteAsync(buffer.AsMemory(0, bytesInBuffer), cancellationToken);
bytesRecieved += bytesInBuffer;
if (progressCallback is object) {
var percent = contentLength is object && contentLength != 0 ? (int)Math.Floor(bytesRecieved / (float)contentLength * 100.0) : 0;
var speedKbSec = (float)((bytesRecieved / 1024.0) / (stopwatch.ElapsedMilliseconds / 1000.0));
var proceed = progressCallback(bytesRecieved, percent, speedKbSec);
if (!proceed) {
httpResponseMessage.ReasonPhrase = "Callback cancelled download";
httpResponseMessage.StatusCode = HttpStatusCode.PartialContent;
return (false, new(httpRequestMessage, httpResponseMessage));
}
}
}
return (true, new(httpRequestMessage, httpResponseMessage));
}
catch (Exception ex) {
if (created) try { File.Delete(fileToWriteTo); } catch { };
return (false, new(httpRequestMessage, ex));
}
}
public static async Task<(string? ResponseAsString, HttpResponse httpResponse)> GetToStringAsync(this HttpClient httpClient, Uri requestUri, CancellationTokenSource? cts = null) {
var httpRequestMessage = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = requestUri };
try {
var cancellationToken = cts?.Token ?? default;
using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken);
if (!httpResponseMessage.IsSuccessStatusCode) return (null, new(httpRequestMessage, httpResponseMessage));
var responseAsString = await httpResponseMessage.Content.ReadAsStringAsync();
return (responseAsString, new(httpRequestMessage, httpResponseMessage));
}
catch (Exception ex) {
return (null, new(httpRequestMessage, ex)); ;
}
}
public static async Task<(string? ResponseAsString, HttpResponse httpResponse)> PostToStringAsync(this HttpClient httpClient, Uri requestUri, HttpContent postBuffer, CancellationTokenSource? cts = null) {
var httpRequestMessage = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = requestUri, Content = postBuffer };
try {
var cancellationToken = cts?.Token ?? default;
using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken);
if (!httpResponseMessage.IsSuccessStatusCode) return (null, new(httpRequestMessage, httpResponseMessage));
var responseAsString = await httpResponseMessage.Content.ReadAsStringAsync();
return (responseAsString, new(httpRequestMessage, httpResponseMessage));
}
catch (Exception ex) {
return (null, new(httpRequestMessage, ex));
}
}
}
}
namespace System {
public static class ExtensionMethods {
public static string FullMessage(this Exception ex) {
if (ex is AggregateException aex) return aex.InnerExceptions.Aggregate("[ ", (total, next) => $"{total}[{next.FullMessage()}] ") + "]";
var msg = ex.Message.Replace(", see inner exception.", "").Trim();
var innerMsg = ex.InnerException?.FullMessage();
if (innerMsg is object && innerMsg!=msg) msg = $"{msg} [ {innerMsg} ]";
return msg;
}
}
}
To use:
// download to file
var lastPercent = 0;
bool progressCallback(long bytesRecieved, int percent, float speedKbSec) {
if (percent > lastPercent) {
lastPercent = percent;
Log($"Downloading... {percent}% {speedKbSec/1024.0:0.00}Mbps");
}
return true;
}
var (success, httpResponse) = await httpClient.DownloadFileAsync(
new(myUrlString),
localFileName,
null, // CancellationTokenSource
progressCallback
);
if (success) {
// file downloaded to localFile, httpResponse.ResponseMessageInfo contain
// extra information ie headers and status code
} else {
Log(httpResponse.ToString()); // human friendly error information
// if httpResponse.ResponseMessageInfo is object then server refused the request -
// examine httpResponse.ResponseMessageInfo.HttpStatusCode etc
// else we had a Http exception - examine httpResponse.ExceptionInfo
}
// Http get
var (responseAsString, httpResponse) = await httpClient.GetToStringAsync(url);
if (responseAsString is object) {
// responseAsString contains the string response from the server
} else {
// as for DownloadFileAsync
}
// http post
var postBuffer = new StringContent(jsonInString, System.Text.Encoding.UTF8, "application/x-www-form-urlencoded");
var (responseAsString, httpResponse) = await httpClient.PostToStringAsync(url, postBuffer);
if (responseAsString is object) {
// responseAsString contains the string response from the server
} else {
Log(httpResponse.ToString()); // human friendly error informaiton
// as for DownloadFileAsync
}
Below code contain logic for download file with original name
private string DownloadFile(string url)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
string filename = "";
string destinationpath = Environment;
if (!Directory.Exists(destinationpath))
{
Directory.CreateDirectory(destinationpath);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponseAsync().Result)
{
string path = response.Headers["Content-Disposition"];
if (string.IsNullOrWhiteSpace(path))
{
var uri = new Uri(url);
filename = Path.GetFileName(uri.LocalPath);
}
else
{
ContentDisposition contentDisposition = new ContentDisposition(path);
filename = contentDisposition.FileName;
}
var responseStream = response.GetResponseStream();
using (var fileStream = File.Create(System.IO.Path.Combine(destinationpath, filename)))
{
responseStream.CopyTo(fileStream);
}
}
return Path.Combine(destinationpath, filename);
}
There are a lot of answers but this is the solution I used recently for .NET 6 or greater.
using var httpClient = new HttpClient();
var tempPath = Path.GetTempFileName();
await using var s = await HttpClient.GetStreamAsync(pdfFilePath);
await using var fs = File.OpenWrite(tempPath);
await s.CopyToAsync(fs);
As per my research I found that WebClient.DownloadFileAsync is the best way to download file. It is available in System.Net namespace and it supports .net core as well.
Here is the sample code to download the file.
using System;
using System.IO;
using System.Net;
using System.ComponentModel;
public class Program
{
public static void Main()
{
new Program().Download("ftp://localhost/test.zip");
}
public void Download(string remoteUri)
{
string FilePath = Directory.GetCurrentDirectory() + "/tepdownload/" + Path.GetFileName(remoteUri); // path where download file to be saved, with filename, here I have taken file name from supplied remote url
using (WebClient client = new WebClient())
{
try
{
if (!Directory.Exists("tepdownload"))
{
Directory.CreateDirectory("tepdownload");
}
Uri uri = new Uri(remoteUri);
//password username of your file server eg. ftp username and password
client.Credentials = new NetworkCredential("username", "password");
//delegate method, which will be called after file download has been complete.
client.DownloadFileCompleted += new AsyncCompletedEventHandler(Extract);
//delegate method for progress notification handler.
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgessChanged);
// uri is the remote url where filed needs to be downloaded, and FilePath is the location where file to be saved
client.DownloadFileAsync(uri, FilePath);
}
catch (Exception)
{
throw;
}
}
}
public void Extract(object sender, AsyncCompletedEventArgs e)
{
Console.WriteLine("File has been downloaded.");
}
public void ProgessChanged(object sender, DownloadProgressChangedEventArgs e)
{
Console.WriteLine($"Download status: {e.ProgressPercentage}%.");
}
}
With above code file will be downloaded inside tepdownload folder of the project's directory. Please read comment in code to understand what above code do.
In the event that you need to set Headers and Cookies to download a file, you will need to do things slightly differently. Here is an example...
// Pass in the HTTPGET URL, Full Path w/Filename, and a populated Cookie Container (optional)
private async Task DownloadFileRequiringHeadersAndCookies(string getUrl, string fullPath, CookieContainer cookieContainer, CancellationToken cancellationToken)
{
cookieContainer ??= new CookieContainer(); // TODO: FILL ME AND PASS ME IN
using (var handler = new HttpClientHandler()
{
UseCookies = true,
CookieContainer = cookieContainer, // This will, both, use the cookies passed in, and update/create cookies from the response
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true, // use only if it gets angry about the SSL endpoints
AllowAutoRedirect = true,
})
{
using (var client = new HttpClient(handler))
{
SetHeaders(client);
using (var response = await client.GetAsync(getUrl, cancellationToken))
{
if (response.IsSuccessStatusCode)
{
var bytes = await response.Content.ReadAsByteArrayAsync(cancellationToken);
await File.WriteAllBytesAsync(fullPath, bytes, cancellationToken); // This overwrites the file
}
else
{
// TODO: HANDLE ME
throw new FileNotFoundException();
}
}
}
}
}
And, to add the Headers you need with this...
private void SetHeaders(HttpClient client)
{
// TODO: SET ME
client.DefaultRequestHeaders.Connection.Add("keep-alive");
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...");
client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9, ...");
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("en-US"));
client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("en", .9));
...
}
ASIDE: You can fill the CookieContainer by:
Looping through the cookies of a previous Response.
This response could be from HttpAgilityPack, or WebClient, or Puppeteer (lots of options)
Manually entries (from config values or hard coded values).
You may need to know the status and update a ProgressBar during the file download or use credentials before making the request.
Here it is, an example that covers these options. Lambda notation and String interpolation has been used:
using System.Net;
// ...
using (WebClient client = new WebClient()) {
Uri ur = new Uri("http://remotehost.do/images/img.jpg");
//client.Credentials = new NetworkCredential("username", "password");
String credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes("Username" + ":" + "MyNewPassword"));
client.Headers[HttpRequestHeader.Authorization] = $"Basic {credentials}";
client.DownloadProgressChanged += (o, e) =>
{
Console.WriteLine($"Download status: {e.ProgressPercentage}%.");
// updating the UI
Dispatcher.Invoke(() => {
progressBar.Value = e.ProgressPercentage;
});
};
client.DownloadDataCompleted += (o, e) =>
{
Console.WriteLine("Download finished!");
};
client.DownloadFileAsync(ur, #"C:\path\newImage.jpg");
}
static void Main(string[] args)
{
DownloadFileAsync().GetAwaiter();
Console.WriteLine("File was downloaded");
Console.Read();
}
private static async Task DownloadFileAsync()
{
WebClient client = new WebClient();
await client.DownloadFileTaskAsync(new Uri("http://somesite.com/myfile.txt"), "mytxtFile.txt");
}
This is my solution, it works fine:
public static void DownloadFile(string url, string pathToSaveFile)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
// or: ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
using (WebDownload client = new WebDownload())
{
client.Headers["User-Agent"] = "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36";
client.DownloadFile(new Uri(url), pathToSaveFile);
}
}
public class WebDownload : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
if (request != null)
{
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
}
return request;
}
}
In .NET Core MVC, you can sometimes do it as simply as:
public async Task<ActionResult> DownloadUrl(string url) {
return Redirect(url);
}
This probably assumes that the MIME type you're trying to download is set to be downloadable by the browser (e.g. .mp4), so it doesn't try to redirect to a webpage.
I have used SO to help with several issues in the past. However, I cannot find a solution to something I have been struggling with for 2 days now.
I am a noob, please be kind :)
I have an app that I created using Xamarin Studio, targeted for Android. It is a basic GET request from a Rest Api. It was working perfectly until I realized I was not helping myself when it came time to create the same app in IOS and Windows. Once I changed my project to utilize a PCL I started getting errors, primarily around my RestClient class (originally got from http://www.codeproject.com/Tips/497123/How-to-make-REST-requests-with-Csharp)
From my droid app class:
var apiUser = GetString(Resource.String.apiUser);
var apiPass = GetString(Resource.String.apiPass);
//Get token from API
string token = authenticate(apiUser,apiPass);
public static string authenticate(string apiUser, string apiPass)
{
Authentication Auth = new Authentication ();
try
{
// set json by passing AuthenticationUrl as endpoint, returns json data
var o = JObject.Parse(EntryRepository.getJson(PJTApiUrls.getAuthenticationUrl(apiUser,apiPass)));
Auth.Token = (string)o["Token"];
return Auth.Token;
}
catch (Exception e)
{
// Couldn't do stuff. Log the exception.
// TODO possible timeout, try again, if fails again then return error message
if (e.Message.Contains("400") || e.Message.Contains("401"))
{
string error = string.Format("Invalid credentials, please try again");
return error;
} else {
string error = string.Format ("An error occurred: \r\n{0}", e.Message);
return error;
}
}
}
getAuthenticationUrl gets the api URL.
Here is getJson (in PCL):
public static string getJson(string endpoint)
{
string apiurl = endpoint;
var client = new _RestClient();
client.EndPoint = apiurl;
client.ContentType = "application/json";
client.Method = HttpVerb.GET;
//client.Method = HttpVerb.POST;
client.PostData = "";
//client.PostData = "{postData: value}";
//client.PostData = "{'someValueToPost': 'The Value being Posted'}";
var json = client._MakeRequestAsync();
// to append parameters, pass them into make request:
//var json = client.MakeRequest("?param=0");
return json.ToString();
}
And for the _RestClient class (in PCL):
public async Task<string> _MakeRequestAsync()
{
try {
var request = _MakeRequestAsync ("");
return await request;
}
catch (Exception e){
return e.Message;
}
}
public async Task<string> _MakeRequestAsync(string parameters)
{
var uri = new Uri(EndPoint + parameters);
var request = WebRequest.Create(uri) as HttpWebRequest;
using (var response = await request.GetResponseAsync () as HttpWebResponse) {
var responseValue = string.Empty;
if (response.StatusCode != HttpStatusCode.OK) {
var message = String.Format ("Request failed. Received HTTP {0}", response.StatusCode);
throw new Exception (message);
}
// grab the response
using (var responseStream = await Task.Factory.FromAsync<Stream>(request.BeginGetRequestStream, request.EndGetRequestStream, null)) {
//using (var responseStream = response.GetResponseStream ()) {
if (responseStream != null)
using (var reader = new StreamReader (responseStream)) {
responseValue = reader.ReadToEnd ();
}
}
return responseValue;
}
}
responseValue is returning null
return await request is saying "Status = Waiting for activation"
I have also had the error: "Unexpected character encountered while parsing value: S. Path '', line 0, position 0."
But this works if the RestClient class is within Droid (Instead of the shared PCL) and contains the following:
public string MakeRequest ()
{
return MakeRequest ("");
}
public string MakeRequest (string parameters)
{
var request = (HttpWebRequest)WebRequest.Create (EndPoint + parameters);
request.Method = Method.ToString ();
request.ContentLength = 0;
request.ContentType = ContentType;
if (!string.IsNullOrEmpty (PostData) && Method == HttpVerb.POST) {
var bytes = Encoding.GetEncoding ("iso-8859-1").GetBytes (PostData);
request.ContentLength = bytes.Length;
using (var writeStream = request.GetRequestStream ()) {
writeStream.Write (bytes, 0, bytes.Length);
}
}
using (var response = (HttpWebResponse)request.GetResponse ()) {
var responseValue = string.Empty;
if (response.StatusCode != HttpStatusCode.OK) {
var message = String.Format ("Request failed. Received HTTP {0}", response.StatusCode);
throw new ApplicationException (message);
}
// grab the response
using (var responseStream = response.GetResponseStream ()) {
if (responseStream != null)
using (var reader = new StreamReader (responseStream)) {
responseValue = reader.ReadToEnd ();
}
}
return responseValue;
}
}
I cannot figure this out, any help/guidance is appreciated. Let me know if I can clarify anything.
***** UPDATE ***** Thanks to #milen-pavlov help thus far, here is where I am currently at:
in Android project:
var apiUser = GetString(Resource.String.apiUser);
var apiPass = GetString(Resource.String.apiPass);
//Get token from API
var token = await authenticate(apiUser,apiPass);
lblOutput.Text = token;
calls (also in Android project):
public static async Task<string> authenticate(string apiUser, string apiPass)
{
Authentication Auth = new Authentication ();
try
{
// set json by passing AuthenticationUrl as endpoint, returns json data
var o = JObject.Parse(await EntryRepository.getJson(PJTApiUrls.getAuthenticationUrl(apiUser,apiPass)));
Auth.Token = (string)o["Token"];
return Auth.Token;
}
catch (Exception e)
{
if (e.Message.Contains("400") || e.Message.Contains("401"))
{
string error = string.Format("Invalid credentials, please try again");
return error;
} else {
string error = string.Format ("An error occurred: \r\n{0}", e.Message);
return error;
}
}
}
Calls json class in PCL project:
public static async Task<string> getJson(string endpoint)
{
string apiurl = endpoint;
var client = new _RestClient();
client.EndPoint = apiurl;
client.ContentType = "application/json";
client.Method = HttpVerb.GET;
client.PostData = "";
var json = await client._MakeRequestAsync();
return json;
}
which then calls restclient class in PCL project:
public async Task<string> _MakeRequestAsync()
{
var request = _MakeRequestAsync ("");
return await request;
}
public async Task<string> _MakeRequestAsync(string parameters)
{
var uri = new Uri(EndPoint + parameters);
using (var client = new HttpClient())
{
var response = await client.GetAsync(uri);
return await response.Content.ReadAsAsync<string>();
};
}
End result/error:
Any guidance is appreciated!
Can you use HttpClient instead?
Sample Get request will look similar to this:
public async Task<string> _MakeRequestAsync(string parameters)
{
var uri = new Uri(EndPoint + parameters);
using (var client = new HttpClient())
{
var response = await client.GetAsync(uri);
return await result.Content.ReadAsStringAsync();
};
}
In the Asp.net MVC controller (GET method) I am calling external web service - for geolocation of IP - returning json data for IP location. How can I make the call to be async, hence the stack can continue while waiting the response from the service. When the GEO IP request finished I want to be able to make update to the db. Here is the current sync code:
public ActionResult SelectFacility(int franchiseId, Guid? coachLoggingTimeStampId)
{
//...
string responseFromServer = Helpers.GetLocationByIPAddress(userIpAddress);
HomeModels.GeoLocationModel myojb = new HomeModels.GeoLocationModel();
if (!String.IsNullOrEmpty(responseFromServer))
{
JavaScriptSerializer js = new JavaScriptSerializer();
myojb = (HomeModels.GeoLocationModel)js.Deserialize(responseFromServer, typeof(HomeModels.GeoLocationModel));
}
//...
}
public static string GetLocationByIPAddress(string ipAddress)
{
Stream resStream = null;
string responseFromServer = "";
try
{
string url = GeoLocationPath.FreeGeoIP + ipAddress;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
resStream = response.GetResponseStream();
StreamReader reader = new StreamReader(resStream);
responseFromServer = reader.ReadToEnd();
return responseFromServer;
}
catch (Exception ex)
{
//TODO handle this
}
finally
{
if (null != resStream)
{
resStream.Flush();
resStream.Close();
}
}
return responseFromServer;
}
Any suggestion - Thread, AsyncTask ?
Thanks
Make your ASP.NET MVC controller asynchronous:
http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4
Then use HttpClient.GetStringAsync and await its result:
public async Task<ActionResult> SelectFacility(
int franchiseId, Guid? coachLoggingTimeStampId)
{
//...
string responseFromServer = await Helpers.GetLocationByIPAddressAsync(
userIpAddress);
//...
}
public static async Task<string> GetLocationByIPAddress(string ipAddress)
{
using (var httpClient = new HttpClient())
return await httpClient.GetStringAsync(
GeoLocationPath.FreeGeoIP + ipAddress);
}
What is a simple way of downloading a file from a URL path?
using (var client = new WebClient())
{
client.DownloadFile("http://example.com/file/song/a.mpeg", "a.mpeg");
}
Include this namespace
using System.Net;
Download Asynchronously and put a ProgressBar to show the status of the download within the UI Thread Itself
private void BtnDownload_Click(object sender, RoutedEventArgs e)
{
using (WebClient wc = new WebClient())
{
wc.DownloadProgressChanged += wc_DownloadProgressChanged;
wc.DownloadFileAsync (
// Param1 = Link of file
new System.Uri("http://www.sayka.com/downloads/front_view.jpg"),
// Param2 = Path to save
"D:\\Images\\front_view.jpg"
);
}
}
// Event to track the progress
void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
Use System.Net.WebClient.DownloadFile:
string remoteUri = "http://www.contoso.com/library/homepage/images/";
string fileName = "ms-banner.gif", myStringWebResource = null;
// Create a new WebClient instance.
using (WebClient myWebClient = new WebClient())
{
myStringWebResource = remoteUri + fileName;
// Download the Web resource and save it into the current filesystem folder.
myWebClient.DownloadFile(myStringWebResource, fileName);
}
using System.Net;
WebClient webClient = new WebClient();
webClient.DownloadFile("http://mysite.com/myfile.txt", #"c:\myfile.txt");
WebRequest, WebClient, and ServicePoint are obsolete from .NET 6 (source - 11/2021).
Use the System.Net.Http.HttpClient class instead:
using (var client = new HttpClient())
{
using (var s = client.GetStreamAsync("https://via.placeholder.com/150"))
{
using (var fs = new FileStream("localfile.jpg", FileMode.OpenOrCreate))
{
s.Result.CopyTo(fs);
}
}
}
Async version of the same code:
using var client = new HttpClient();
using var s = await client.GetStreamAsync("https://via.placeholder.com/150");
using var fs = new FileStream("localfile.jpg", FileMode.OpenOrCreate);
await s.CopyToAsync(fs);
Complete class to download a file while printing status to console.
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Threading;
class FileDownloader
{
private readonly string _url;
private readonly string _fullPathWhereToSave;
private bool _result = false;
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(0);
public FileDownloader(string url, string fullPathWhereToSave)
{
if (string.IsNullOrEmpty(url)) throw new ArgumentNullException("url");
if (string.IsNullOrEmpty(fullPathWhereToSave)) throw new ArgumentNullException("fullPathWhereToSave");
this._url = url;
this._fullPathWhereToSave = fullPathWhereToSave;
}
public bool StartDownload(int timeout)
{
try
{
System.IO.Directory.CreateDirectory(Path.GetDirectoryName(_fullPathWhereToSave));
if (File.Exists(_fullPathWhereToSave))
{
File.Delete(_fullPathWhereToSave);
}
using (WebClient client = new WebClient())
{
var ur = new Uri(_url);
// client.Credentials = new NetworkCredential("username", "password");
client.DownloadProgressChanged += WebClientDownloadProgressChanged;
client.DownloadFileCompleted += WebClientDownloadCompleted;
Console.WriteLine(#"Downloading file:");
client.DownloadFileAsync(ur, _fullPathWhereToSave);
_semaphore.Wait(timeout);
return _result && File.Exists(_fullPathWhereToSave);
}
}
catch (Exception e)
{
Console.WriteLine("Was not able to download file!");
Console.Write(e);
return false;
}
finally
{
this._semaphore.Dispose();
}
}
private void WebClientDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
Console.Write("\r --> {0}%.", e.ProgressPercentage);
}
private void WebClientDownloadCompleted(object sender, AsyncCompletedEventArgs args)
{
_result = !args.Cancelled;
if (!_result)
{
Console.Write(args.Error.ToString());
}
Console.WriteLine(Environment.NewLine + "Download finished!");
_semaphore.Release();
}
public static bool DownloadFile(string url, string fullPathWhereToSave, int timeoutInMilliSec)
{
return new FileDownloader(url, fullPathWhereToSave).StartDownload(timeoutInMilliSec);
}
}
Usage:
static void Main(string[] args)
{
var success = FileDownloader.DownloadFile(fileUrl, fullPathWhereToSave, timeoutInMilliSec);
Console.WriteLine("Done - success: " + success);
Console.ReadLine();
}
Try using this:
private void downloadFile(string url)
{
string file = System.IO.Path.GetFileName(url);
WebClient cln = new WebClient();
cln.DownloadFile(url, file);
}
Also you can use DownloadFileAsync method in WebClient class. It downloads to a local file the resource with the specified URI. Also this method does not block the calling thread.
Sample:
webClient.DownloadFileAsync(new Uri("http://www.example.com/file/test.jpg"), "test.jpg");
For more information:
http://csharpexamples.com/download-files-synchronous-asynchronous-url-c/
Check for a network connection using GetIsNetworkAvailable() to avoid creating empty files when not connected to a network.
if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
{
using (System.Net.WebClient client = new System.Net.WebClient())
{
client.DownloadFileAsync(new Uri("http://www.examplesite.com/test.txt"),
"D:\\test.txt");
}
}
WebClient is obsolete
If you want to download to a file avoid first reading to memory by using ResponseHeadersRead like this:
static public async Task HttpDownloadFileAsync(HttpClient httpClient, string url, string fileToWriteTo) {
using HttpResponseMessage response = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
using Stream streamToReadFrom = await response.Content.ReadAsStreamAsync();
using Stream streamToWriteTo = File.Open(fileToWriteTo, FileMode.Create);
await streamToReadFrom.CopyToAsync(streamToWriteTo);
}
Above code is more of an outline, adding correct error/exception handling is not trivial, also progress report is not trivial, as is Disposing.
I came up with a set of C# 9.0 extension classes for DownoadFileAsync, GetToStringAsync and PostToStringAsync
namespace System.Net.Http {
// HttpResponse is in one of 3 states:
// - ResponseMessageInfo is object && ResponseMessageInfo.IsSuccessStatusCode -> success, inspect ResponseMessageInfo for StatusCode etc
// - ResponseMessageInfo is object && !ResponseMessageInfo.IsSuccessStatusCode -> failure, inspect ResponseMessageInfo for StatusCode, ReasonPhrase etc
// - ResponseMessageInfo is null -> exception, inspect ExceptionInfo fields
public record HttpResponse {
// copies of HttpRequestMessage and HttpResponseMessage which do not have the content and do not need to be disposed
public record HttpRequestMessageInfo(HttpRequestHeaders Headers, HttpMethod Method, HttpRequestOptions Options, Uri? RequestUri, Version Version, HttpVersionPolicy VersionPolicy);
public record HttpResponseMessageInfo(HttpResponseHeaders Headers, bool IsSuccessStatusCode, string? ReasonPhrase, HttpRequestMessageInfo RequestMessage, HttpStatusCode StatusCode, HttpResponseHeaders TrailingHeaders, Version Version);
// holds Http exception information
public record HttpExceptionInfo(HttpRequestMessageInfo HttpRequestMessage, string ErrorMessage, WebExceptionStatus? WebExceptionStatus);
// if ResponseMessageInfo is null ExceptionInfo is not and vice versa
public HttpResponseMessageInfo? ResponseMessageInfo { get; init; }
public HttpExceptionInfo? ExceptionInfo { get; init; }
public HttpResponse(HttpRequestMessage requestMessage, HttpResponseMessage responseMessage) {
var requestMessageInfo = new HttpRequestMessageInfo(requestMessage.Headers, requestMessage.Method, requestMessage.Options, requestMessage.RequestUri, requestMessage.Version, requestMessage.VersionPolicy);
ResponseMessageInfo = new(responseMessage.Headers, responseMessage.IsSuccessStatusCode, responseMessage.ReasonPhrase, requestMessageInfo, responseMessage.StatusCode, responseMessage.TrailingHeaders, responseMessage.Version);
ExceptionInfo = null;
}
public HttpResponse(HttpRequestMessage requestMessage, Exception exception) {
ResponseMessageInfo = null;
var requestMessageInfo = new HttpRequestMessageInfo(requestMessage.Headers, requestMessage.Method, requestMessage.Options, requestMessage.RequestUri, requestMessage.Version, requestMessage.VersionPolicy);
if (exception is WebException ex1 && ex1.Status == WebExceptionStatus.ProtocolError) {
using HttpWebResponse? httpResponse = (HttpWebResponse?)ex1.Response;
ExceptionInfo = new(requestMessageInfo, httpResponse?.StatusDescription ?? "", ex1.Status);
}
else if (exception is WebException ex2) ExceptionInfo = new(requestMessageInfo, ex2.FullMessage(), ex2.Status);
else if (exception is TaskCanceledException ex3 && ex3.InnerException is TimeoutException) ExceptionInfo = new(requestMessageInfo, ex3.InnerException.FullMessage(), WebExceptionStatus.Timeout);
else if (exception is TaskCanceledException ex4) ExceptionInfo = new(requestMessageInfo, ex4.FullMessage(), WebExceptionStatus.RequestCanceled);
else ExceptionInfo = new(requestMessageInfo, exception.FullMessage(), null);
}
public override string ToString() {
if (ResponseMessageInfo is object) {
var msg = ResponseMessageInfo.IsSuccessStatusCode ? "Success" : "Failure";
msg += $" {Enum.GetName(typeof(HttpStatusCode), ResponseMessageInfo.StatusCode)}";
if (ResponseMessageInfo.ReasonPhrase is object) msg += $" {ResponseMessageInfo.ReasonPhrase}";
return msg;
} else if (ExceptionInfo is object) {
var msg = "Failure";
msg += $" {ExceptionInfo.ErrorMessage}";
if (ExceptionInfo.WebExceptionStatus is object) msg += $" {Enum.GetName(typeof(WebExceptionStatus), ExceptionInfo.WebExceptionStatus)}";
return msg;
}
return "NA"; // never reach here
}
}
public static class ExtensionMethods {
// progressCallback recieves (bytesRecieved, percent, speedKbSec) and can return false to cancell download
public static async Task<(bool success, HttpResponse httpResponse)> DownloadFileAsync(this HttpClient httpClient, Uri requestUri, string fileToWriteTo, CancellationTokenSource? cts = null, Func<long, int, float, bool>? progressCallback = null) {
var httpRequestMessage = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = requestUri };
var created = false;
try {
var cancellationToken = cts?.Token ?? default;
using HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
if (!httpResponseMessage.IsSuccessStatusCode) return (false, new(httpRequestMessage, httpResponseMessage));
var contentLength = httpResponseMessage.Content.Headers.ContentLength;
using Stream streamToReadFrom = await httpResponseMessage.Content.ReadAsStreamAsync();
using Stream streamToWriteTo = File.Open(fileToWriteTo, FileMode.Create);
created = true;
var buffer = new byte[81920];
var bytesRecieved = (long)0;
var stopwatch = Stopwatch.StartNew();
int bytesInBuffer;
while ((bytesInBuffer = await streamToReadFrom.ReadAsync(buffer, cancellationToken)) != 0) {
await streamToWriteTo.WriteAsync(buffer.AsMemory(0, bytesInBuffer), cancellationToken);
bytesRecieved += bytesInBuffer;
if (progressCallback is object) {
var percent = contentLength is object && contentLength != 0 ? (int)Math.Floor(bytesRecieved / (float)contentLength * 100.0) : 0;
var speedKbSec = (float)((bytesRecieved / 1024.0) / (stopwatch.ElapsedMilliseconds / 1000.0));
var proceed = progressCallback(bytesRecieved, percent, speedKbSec);
if (!proceed) {
httpResponseMessage.ReasonPhrase = "Callback cancelled download";
httpResponseMessage.StatusCode = HttpStatusCode.PartialContent;
return (false, new(httpRequestMessage, httpResponseMessage));
}
}
}
return (true, new(httpRequestMessage, httpResponseMessage));
}
catch (Exception ex) {
if (created) try { File.Delete(fileToWriteTo); } catch { };
return (false, new(httpRequestMessage, ex));
}
}
public static async Task<(string? ResponseAsString, HttpResponse httpResponse)> GetToStringAsync(this HttpClient httpClient, Uri requestUri, CancellationTokenSource? cts = null) {
var httpRequestMessage = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = requestUri };
try {
var cancellationToken = cts?.Token ?? default;
using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken);
if (!httpResponseMessage.IsSuccessStatusCode) return (null, new(httpRequestMessage, httpResponseMessage));
var responseAsString = await httpResponseMessage.Content.ReadAsStringAsync();
return (responseAsString, new(httpRequestMessage, httpResponseMessage));
}
catch (Exception ex) {
return (null, new(httpRequestMessage, ex)); ;
}
}
public static async Task<(string? ResponseAsString, HttpResponse httpResponse)> PostToStringAsync(this HttpClient httpClient, Uri requestUri, HttpContent postBuffer, CancellationTokenSource? cts = null) {
var httpRequestMessage = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = requestUri, Content = postBuffer };
try {
var cancellationToken = cts?.Token ?? default;
using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken);
if (!httpResponseMessage.IsSuccessStatusCode) return (null, new(httpRequestMessage, httpResponseMessage));
var responseAsString = await httpResponseMessage.Content.ReadAsStringAsync();
return (responseAsString, new(httpRequestMessage, httpResponseMessage));
}
catch (Exception ex) {
return (null, new(httpRequestMessage, ex));
}
}
}
}
namespace System {
public static class ExtensionMethods {
public static string FullMessage(this Exception ex) {
if (ex is AggregateException aex) return aex.InnerExceptions.Aggregate("[ ", (total, next) => $"{total}[{next.FullMessage()}] ") + "]";
var msg = ex.Message.Replace(", see inner exception.", "").Trim();
var innerMsg = ex.InnerException?.FullMessage();
if (innerMsg is object && innerMsg!=msg) msg = $"{msg} [ {innerMsg} ]";
return msg;
}
}
}
To use:
// download to file
var lastPercent = 0;
bool progressCallback(long bytesRecieved, int percent, float speedKbSec) {
if (percent > lastPercent) {
lastPercent = percent;
Log($"Downloading... {percent}% {speedKbSec/1024.0:0.00}Mbps");
}
return true;
}
var (success, httpResponse) = await httpClient.DownloadFileAsync(
new(myUrlString),
localFileName,
null, // CancellationTokenSource
progressCallback
);
if (success) {
// file downloaded to localFile, httpResponse.ResponseMessageInfo contain
// extra information ie headers and status code
} else {
Log(httpResponse.ToString()); // human friendly error information
// if httpResponse.ResponseMessageInfo is object then server refused the request -
// examine httpResponse.ResponseMessageInfo.HttpStatusCode etc
// else we had a Http exception - examine httpResponse.ExceptionInfo
}
// Http get
var (responseAsString, httpResponse) = await httpClient.GetToStringAsync(url);
if (responseAsString is object) {
// responseAsString contains the string response from the server
} else {
// as for DownloadFileAsync
}
// http post
var postBuffer = new StringContent(jsonInString, System.Text.Encoding.UTF8, "application/x-www-form-urlencoded");
var (responseAsString, httpResponse) = await httpClient.PostToStringAsync(url, postBuffer);
if (responseAsString is object) {
// responseAsString contains the string response from the server
} else {
Log(httpResponse.ToString()); // human friendly error informaiton
// as for DownloadFileAsync
}
Below code contain logic for download file with original name
private string DownloadFile(string url)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
string filename = "";
string destinationpath = Environment;
if (!Directory.Exists(destinationpath))
{
Directory.CreateDirectory(destinationpath);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponseAsync().Result)
{
string path = response.Headers["Content-Disposition"];
if (string.IsNullOrWhiteSpace(path))
{
var uri = new Uri(url);
filename = Path.GetFileName(uri.LocalPath);
}
else
{
ContentDisposition contentDisposition = new ContentDisposition(path);
filename = contentDisposition.FileName;
}
var responseStream = response.GetResponseStream();
using (var fileStream = File.Create(System.IO.Path.Combine(destinationpath, filename)))
{
responseStream.CopyTo(fileStream);
}
}
return Path.Combine(destinationpath, filename);
}
There are a lot of answers but this is the solution I used recently for .NET 6 or greater.
using var httpClient = new HttpClient();
var tempPath = Path.GetTempFileName();
await using var s = await HttpClient.GetStreamAsync(pdfFilePath);
await using var fs = File.OpenWrite(tempPath);
await s.CopyToAsync(fs);
As per my research I found that WebClient.DownloadFileAsync is the best way to download file. It is available in System.Net namespace and it supports .net core as well.
Here is the sample code to download the file.
using System;
using System.IO;
using System.Net;
using System.ComponentModel;
public class Program
{
public static void Main()
{
new Program().Download("ftp://localhost/test.zip");
}
public void Download(string remoteUri)
{
string FilePath = Directory.GetCurrentDirectory() + "/tepdownload/" + Path.GetFileName(remoteUri); // path where download file to be saved, with filename, here I have taken file name from supplied remote url
using (WebClient client = new WebClient())
{
try
{
if (!Directory.Exists("tepdownload"))
{
Directory.CreateDirectory("tepdownload");
}
Uri uri = new Uri(remoteUri);
//password username of your file server eg. ftp username and password
client.Credentials = new NetworkCredential("username", "password");
//delegate method, which will be called after file download has been complete.
client.DownloadFileCompleted += new AsyncCompletedEventHandler(Extract);
//delegate method for progress notification handler.
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgessChanged);
// uri is the remote url where filed needs to be downloaded, and FilePath is the location where file to be saved
client.DownloadFileAsync(uri, FilePath);
}
catch (Exception)
{
throw;
}
}
}
public void Extract(object sender, AsyncCompletedEventArgs e)
{
Console.WriteLine("File has been downloaded.");
}
public void ProgessChanged(object sender, DownloadProgressChangedEventArgs e)
{
Console.WriteLine($"Download status: {e.ProgressPercentage}%.");
}
}
With above code file will be downloaded inside tepdownload folder of the project's directory. Please read comment in code to understand what above code do.
In the event that you need to set Headers and Cookies to download a file, you will need to do things slightly differently. Here is an example...
// Pass in the HTTPGET URL, Full Path w/Filename, and a populated Cookie Container (optional)
private async Task DownloadFileRequiringHeadersAndCookies(string getUrl, string fullPath, CookieContainer cookieContainer, CancellationToken cancellationToken)
{
cookieContainer ??= new CookieContainer(); // TODO: FILL ME AND PASS ME IN
using (var handler = new HttpClientHandler()
{
UseCookies = true,
CookieContainer = cookieContainer, // This will, both, use the cookies passed in, and update/create cookies from the response
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true, // use only if it gets angry about the SSL endpoints
AllowAutoRedirect = true,
})
{
using (var client = new HttpClient(handler))
{
SetHeaders(client);
using (var response = await client.GetAsync(getUrl, cancellationToken))
{
if (response.IsSuccessStatusCode)
{
var bytes = await response.Content.ReadAsByteArrayAsync(cancellationToken);
await File.WriteAllBytesAsync(fullPath, bytes, cancellationToken); // This overwrites the file
}
else
{
// TODO: HANDLE ME
throw new FileNotFoundException();
}
}
}
}
}
And, to add the Headers you need with this...
private void SetHeaders(HttpClient client)
{
// TODO: SET ME
client.DefaultRequestHeaders.Connection.Add("keep-alive");
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...");
client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9, ...");
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("en-US"));
client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("en", .9));
...
}
ASIDE: You can fill the CookieContainer by:
Looping through the cookies of a previous Response.
This response could be from HttpAgilityPack, or WebClient, or Puppeteer (lots of options)
Manually entries (from config values or hard coded values).
You may need to know the status and update a ProgressBar during the file download or use credentials before making the request.
Here it is, an example that covers these options. Lambda notation and String interpolation has been used:
using System.Net;
// ...
using (WebClient client = new WebClient()) {
Uri ur = new Uri("http://remotehost.do/images/img.jpg");
//client.Credentials = new NetworkCredential("username", "password");
String credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes("Username" + ":" + "MyNewPassword"));
client.Headers[HttpRequestHeader.Authorization] = $"Basic {credentials}";
client.DownloadProgressChanged += (o, e) =>
{
Console.WriteLine($"Download status: {e.ProgressPercentage}%.");
// updating the UI
Dispatcher.Invoke(() => {
progressBar.Value = e.ProgressPercentage;
});
};
client.DownloadDataCompleted += (o, e) =>
{
Console.WriteLine("Download finished!");
};
client.DownloadFileAsync(ur, #"C:\path\newImage.jpg");
}
static void Main(string[] args)
{
DownloadFileAsync().GetAwaiter();
Console.WriteLine("File was downloaded");
Console.Read();
}
private static async Task DownloadFileAsync()
{
WebClient client = new WebClient();
await client.DownloadFileTaskAsync(new Uri("http://somesite.com/myfile.txt"), "mytxtFile.txt");
}
This is my solution, it works fine:
public static void DownloadFile(string url, string pathToSaveFile)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
// or: ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
using (WebDownload client = new WebDownload())
{
client.Headers["User-Agent"] = "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36";
client.DownloadFile(new Uri(url), pathToSaveFile);
}
}
public class WebDownload : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
if (request != null)
{
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
}
return request;
}
}
In .NET Core MVC, you can sometimes do it as simply as:
public async Task<ActionResult> DownloadUrl(string url) {
return Redirect(url);
}
This probably assumes that the MIME type you're trying to download is set to be downloadable by the browser (e.g. .mp4), so it doesn't try to redirect to a webpage.