I use HTTP GET that downloads a zip file in a browser, something like https://example.com/up/DBID/a/rRID/eFID/vVID (not the exact url)
Now, when I try to do the same download in C# code(same GET method as above) for a desktop application, the zip file downloaded is not a valid archive file. When I opened this file in notepad, it was some HTML page.
I think I'm not setting some header correctly. I looked around for examples. I'd found several wrt uploads, but did not see anything for downloads.
Code:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.ContentType = "application/zip";
try
{
HttpWebResponse res = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(res.GetResponseStream(), System.Text.Encoding.Default))
{
StreamWriter oWriter = new StreamWriter(#"D:\Downloads\1.zip");
oWriter.Write(sr.ReadToEnd());
oWriter.Close();
}
res.Close();
}
catch (Exception ex)
{
}
It's mainly because you use a StreamWriter : TextWriter to handle a binary Zip file. A StreamWriter expects text and will apply an Encoding. And even the simple ASCII Encoder might try to 'fix' what it thinks are invalid line-endings.
You can replace all your code with:
using (var client = new WebClient())
{
client.DownloadFile("http://something", #"D:\Downloads\1.zip");
}
Note that for new code you should look at HttpClient instead of WebClient.
And then don't use using( ) { }
You could just use WebClient for a 2-liner:
using(WebClient wc = new WebClient())
{
wc.DownloadFile(url, #"D:\Downloads\1.zip");
}
You can also use System.Net.Http.HttpClient
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.GetAsync(downloadURL))
{
using(var stream = await response.Content.ReadAsStreamAsync())
{
using(Stream zip = FileManager.OpenWrite(ZIP_PATH))
{
stream.CopyTo(zip);
}
}
}
}
Expanding on Ruben's answer which uses HttpClient instead of WebClient, you can add as an extension method like this:
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
public static class Extensions
{
public static async Task DownloadFile (this HttpClient client, string address, string fileName) {
using (var response = await client.GetAsync(address))
using (var stream = await response.Content.ReadAsStreamAsync())
using (var file = File.OpenWrite(fileName)) {
stream.CopyTo(file);
}
}
}
And then use like this:
var archivePath = "https://api.github.com/repos/microsoft/winget-pkgs/zipball/";
using (var httpClient = new HttpClient())
{
await httpClient.DownloadFile(archivePath, "./repo.zip");
}
Related
Let me preface by stating that I' somewhat new to dealing with zipping/unzipping/reading/reading files. That being said, I'm doing a PoC that will retrieve data via api and write the responses to a database. The response is a zip file and inside this zip is the json data I will be reading and writing to the database.
I'm having some trouble unzipping and reading the information. Please find the code below:
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(baseUrl),
Headers =
{
{ "X-API-TOKEN", apiKey },
},
};
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
// here is where I am stuck - not sure how I would unzip and read the contents
}
Thanks
Assuming you actually have a .zip file, you don't need a MemoryStream, you just need to pass the existing stream to ZipArchive
static HttpClient client = new HttpClient(); // always keep static client
async Task GetZip()
{
using var request = new HttpRequestMessage(HttpMethod.Get, new Uri(baseUrl))
{
Headers = {
{ "X-API-TOKEN", apiKey },
},
};
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
using var stream = await response.Content.ReadAsStreamAsync();
await ProcessZip(stream);
}
async Task ProcessZip(Stream zipStream)
{
using var zip = new ZipArchive(zipStream, ZipArchiveMode.Read);
foreach (var file in zip.Entries)
{
using var entryStream = file.Open();
await ....; // do stuff here
}
}
You can convert body to a byte array and then unzip it using MemoryStream.
byte[] bytes = Encoding.ASCII.GetBytes(body);
using (var mso = new MemoryStream(bytes)) {
using (var gs = new GZipStream(msi, CompressionMode.Decompress)) {
CopyTo(gs, mso);
}
return Encoding.UTF8.GetString(mso.ToArray());
}
I am building an C# Console Application for GET file which will automatically download the file when I run the console application.
These are my codes:
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace WebAPIConsoleNEW
{
class Program
{
static void Main(string[] args)
{
RunAsync().Wait();
}
static async Task RunAsync()
{
string bookPath_Pdf = #"D:\VisualStudio\randomfile.pdf";
string bookPath_xls = #"D:\VisualStudio\randomfile.xls";
string bookPath_doc = #"D:\VisualStudio\randomfile.docx";
string bookPath_zip = #"D:\VisualStudio\randomfile.zip";
string format = "pdf";
string reqBook = format.ToLower() == "pdf" ? bookPath_Pdf : (format.ToLower() == "xls" ? bookPath_xls : (format.ToLower() == "doc" ? bookPath_doc : bookPath_zip));
string fileName = "sample." + format.ToLower();
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:49209/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("applicaiton/json"));
Console.WriteLine("GET");
//converting Pdf file into bytes array
var dataBytes = File.ReadAllBytes(reqBook);
//adding bytes to memory stream
var dataStream = new MemoryStream(dataBytes);
//send request asynchronously
HttpResponseMessage response = await client.GetAsync("api/person");
response.Content = new StreamContent(dataStream);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = fileName;
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
//Check that response was successful or throw exception
//response.EnsureSuccessStatusCode();
//Read response asynchronously and save asynchronously to file
if (response.IsSuccessStatusCode)
{
using (var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:49209/api"))
{
using (
Stream contentStream = await (await client.SendAsync(request)).Content.ReadAsStreamAsync(),
fileStream = new FileStream("D:\\VisualStudio\\randomfile.pdf", FileMode.Create, FileAccess.Write, FileShare.None))
{
//copy the content from response to filestream
await response.Content.CopyToAsync(fileStream);
//Console.WriteLine();
}
}
}
}
catch (HttpRequestException rex)
{
Console.WriteLine(rex.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
When I run another ASP.NET application which is my localhost, it only return the default which is value1 and value2 in the Controller. However, I do not have Controller in C# Console Application. I think I just one step away, I had successfully obtain the file and CopyToAsync the file I wanted to download.
Conclusion:
I want when user runs the application it would straight download the file in a place (or can I use SaveFileDialog to let user decide where to save the file).
Please help thanks
Update:
At first, I created a ASP.NET Web Application and Create a PersonController and I run the Project. After that I created a console C# Application and then I want to achieve the result of when user runs the console C# Application it would straight download the file to a specific place.
In the first get I uses api/person, and I convert the file int o bytes array and add the bytes array to memory stream. After that, I don't really know what I'm doing is right or wrong. I saw something like CopyToAsync is working then I tried it and implement it but it won't works. My goal is simple I just want to acheive once I run the C# Console application it would straight download the file from a specific localhost address
Well I think your problem is that you are sending two GET requests, in case you just want to call api/student then save the response into a file then no need for the second request
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:49209/api")//no need for it
So your code should be like this:
static async Task RunAsync()
{
string bookPath_Pdf = #"D:\VisualStudio\randomfile.pdf";
string bookPath_xls = #"D:\VisualStudio\randomfile.xls";
string bookPath_doc = #"D:\VisualStudio\randomfile.docx";
string bookPath_zip = #"D:\VisualStudio\randomfile.zip";
string format = "pdf";
string reqBook = format.ToLower() == "pdf" ? bookPath_Pdf : (format.ToLower() == "xls" ? bookPath_xls : (format.ToLower() == "doc" ? bookPath_doc : bookPath_zip));
string fileName = "sample." + format.ToLower();
try
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:49209/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("applicaiton/json"));
Console.WriteLine("GET");
//converting Pdf file into bytes array
var dataBytes = File.ReadAllBytes(reqBook);
//adding bytes to memory stream
var dataStream = new MemoryStream(dataBytes);
//send request asynchronously
HttpResponseMessage response = await client.GetAsync("api/person");
response.Content = new StreamContent(dataStream);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = fileName;
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
//Check that response was successful or throw exception
//response.EnsureSuccessStatusCode();
//Read response asynchronously and save asynchronously to file
if (response.IsSuccessStatusCode)
{
using (Stream contentStream = await response.Content.ReadAsStreamAsync())
{
using (fileStream = new FileStream("D:\\VisualStudio\\randomfile.pdf", FileMode.Create, FileAccess.Write, FileShare.None))
{
//copy the content from response to filestream
await response.Content.CopyToAsync(fileStream);
//Console.WriteLine();
}
}
}
}
}
catch (HttpRequestException rex)
{
Console.WriteLine(rex.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Also it would be good to print a message for the user telling him that logging data from server into file(File path) is in progress:
static void Main(string[] args)
{
Console.WriteLine("Logging data from server into file (D:\\VisualStudio\\randomfile.pdf");
RunAsync().Wait();
}
I am using C# to write a small console app. The app needs to make a call to an API service to login.
I was able to make the call successfully. But, I am unable to decode the response
Here is my code
using (var client = new WebClient())
{
client.Headers.Add("User-Agent", "Console App");
client.Headers.Add("RETS-Version", "RETS/1.7.2");
client.Headers.Add("Accept-Encoding", "gzip");
client.Headers.Add("Accept", "*/*");
client.Credentials = new NetworkCredential("username", "password");
try
{
var response = client.DownloadData("url/login.ashx");
MemoryStream stream = new MemoryStream(response);
using (var stram = new GZipStream(stream, CompressionMode.Decompress ))
using (var file = File.Create("../../../Downloads/login_result.txt"))
{
stream.CopyTo(file);
}
} catch(Exception e)
{
}
}
However, the data that gets written to the login_result.txt file looks something like this
‹ í½`I–%&/mÊ{JõJ×àt¡€`$Ø#ìÁˆÍæ’ìiG#)«*ÊeVe]f#Ìí¼÷Þ{ï½÷Þ{ï½÷º;N'÷ßÿ?\fdlöÎ
How can I correctly decode the response?
Probably you should copy GZip decompressed stream and not the memory one:
using (var stram = new GZipStream(stream, CompressionMode.Decompress ))
using (var file = File.Create("../../../Downloads/login_result.txt"))
{
stram.CopyTo(file); //stream.CopyTo(file);
}
I have no access to Sharepoint server, only like standard user from web page. I can upload manually there my documents. I tried to solve it via C# and I complet any code from examples from net. Our Sharepoint is 2007. My code run without any error. I put there control text to see if its proceed. All runs fine but nothing happens in Sharepoint page, no doc is uploaded. I have no idea why its do nothing :)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
namespace Sharepoint
{
class Program
{
public static void CopyStream(Stream read, Stream write)
{
int len; byte[] temp = new byte[1024];
while ((len = read.Read(temp, 0, temp.Length)) > 0)
{
write.Write(temp, 0, len);
/// Console.WriteLine("test");
}
}
static void Main(string[] args)
{
Uri destUri = new Uri("http://gaja/mBreSKCZ/mreports/sales/reportysales/Test_new.txt");
using (FileStream inStream = File.OpenRead(#"C:\Users\TK20382\Test_new.txt"))
{
WebRequest req = WebRequest.Create(destUri);
req.Method = "PUT";
req.Credentials = CredentialCache.DefaultCredentials; // assuming windows Auth
Console.WriteLine("test");
Console.ReadKey();
using (Stream outStream = req.GetRequestStream())
{
CopyStream(inStream, outStream);
}
}
}
}
}
You are missing HttpWebRequest.GetResponse Method which basically invokes PUT request. In addition if you are targeting .NET Framework >=2.0 version, then CopyStream method could be omitted and the line:
CopyStream(inStream, outStream);
replaced with:
inStream.CopyTo(outStream);
Modified version
public static string UploadFile(string targetUrl,ICredentials credentials, string sourcePath)
{
var request = WebRequest.Create(targetUrl);
request.Method = "PUT";
request.Credentials = credentials;
using (var fileStream = File.OpenRead(sourcePath))
using (var requestStream = request.GetRequestStream())
{
fileStream.CopyTo(requestStream);
}
using (var response = request.GetResponse())
using (var reader = new StreamReader(response.GetResponseStream()))
{
return reader.ReadToEnd();
}
}
Usage
UploadFile("https://contoso.intranet.com/documents/guide.docx", CredentialCache.DefaultCredentials, #"D:\guide.docx");
Alternatively WebClient.UploadFile Method could be utilized as shown below:
public static void UploadFile(string targeUrl, ICredentials credentials, string fileName)
{
using (var client = new WebClient())
{
client.Credentials = credentials;
client.UploadFile(targeUrl, "PUT", fileName);
}
}
I have a method:
private bool UploadFile(Stream fileStream, string fileName)
{
HttpContent fileStreamContent = new StreamContent(fileStream);
using (var client = new HttpClient())
{
using (var formData = new MultipartFormDataContent())
{
formData.Add(fileStreamContent, fileName, fileName);
var response = client.PostAsync("url", formData).Result;
return response.StatusCode == HttpStatusCode.OK;
}
}
}
}
That is sending the file to a WCF service, but looking at the Wireshark log of the post, the fileStream isn't being appended, just the filename. Do I need to do something else?
Use a ByteArrayContent instead of a stream content.
var fileContent = new ByteArrayContent(File.ReadAllBytes(fileName));
Then specify your content disposition header:
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
formData.Add(fileContent);
Turns out the fileStream wasn't getting to the method. Using context.Request.Files[0].InputStream seemed to be the culprite. Using .SaveAs and then reading it in as a byteArray and attaching that to the MultiPartFormDataContent worked.