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();
}
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 sending multiples files from my web api but I want to read each part of the stream to convert him into a byte array , then at the end I have a list of byte[], and I can save each files:
[Route("GetFiles")]
public HttpResponseMessage GetFile([FromUri] List<string> filesNames)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
if (filesNames.Count == 0)
return Request.CreateResponse(HttpStatusCode.BadRequest);
var content = new MultipartContent();
filesNames.ForEach(delegate (string fileName)
{
string filePath = System.Web.Hosting.HostingEnvironment.MapPath("~/Uploads/" + fileName);
byte[] pdf = File.ReadAllBytes(filePath);
content.Add(new ByteArrayContent(pdf));
response.Headers.Add(fileName, fileName);
});
var files = JsonConvert.SerializeObject(content);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
response = Request.CreateResponse(HttpStatusCode.OK, content);
return response;
}
Here is how I get one file into a stream, then convert him into a byte array to report the process percentage :
public static async Task<byte[]> CreateDownloadTaskForFile(string urlToDownload, IProgress<DownloadBytesProgress> progessReporter)
{
int receivedBytes = 0;
int totalBytes = 0;
WebClient client = new WebClient();
using (var stream = await client.OpenReadTaskAsync(urlToDownload))
{
byte[] buffer = new byte[BufferSize];
totalBytes = Int32.Parse(client.ResponseHeaders[HttpResponseHeader.ContentLength]);
using (MemoryStream memoryStream = new MemoryStream())
{
for (; ; )
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
await Task.Yield();
break;
}
receivedBytes += bytesRead;
if (progessReporter != null)
{
DownloadBytesProgress args = new DownloadBytesProgress(urlToDownload, receivedBytes, totalBytes);
progessReporter.Report(args);
}
}
return memoryStream.ToArray();
}
}
}
How do I get the position of a stream for each files send ?
Update :
I made a HttpResponseMessage like this :
[Route("GetFiles")]
public HttpResponseMessage GetFiles([FromUri] List<string> filesNames)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
if (filesNames.Count == 0)
return Request.CreateResponse(HttpStatusCode.BadRequest);
var content = new MultipartFormDataContent();
filesNames.ForEach(delegate (string fileName)
{
string filePath = System.Web.Hosting.HostingEnvironment.MapPath("~/Uploads/" + fileName);
byte[] pdf = File.ReadAllBytes(filePath);
content.Add(new ByteArrayContent(pdf), fileName);
});
response = Request.CreateResponse(HttpStatusCode.OK, content);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
}
But from my device side : When I am trying to run the request But there is nothing on the response content :
using (var httpResponseMessage = await httpClient.GetAsync(urlToDownload + filesNamesArg))
{
var streamProvider = new MultipartMemoryStreamProvider();
streamProvider = httpResponseMessage.Content.ReadAsMultipartAsync().Result;
}
Could you show me some docs or advice ?
What?
This answer provides a 100% working example for:
Serving multiple files as a single response from a web API using multipart/mixed content type,
Reading the file contents on the client by parsing the response of the web API implemented in 1
I hope this helps.
Server:
The server application is a .Net 4.7.2 MVC project with web API support.
The following method is implemented in an ApiController and returns all the files under the ~/Uploads folder in a single response.
Please make note of the use of Request.RegisterForDispose extension to register the FileStreams for later disposal.
public async Task<HttpResponseMessage> GetFiles()
{
string filesPath = System.Web.Hosting.HostingEnvironment.MapPath("~/Uploads");
List<string> fileNames = new List<string>(Directory.GetFiles(filesPath));
var content = new MultipartContent();
fileNames.ForEach(delegate(string fileName)
{
var fileContent = new StreamContent(File.OpenRead(fileName));
Request.RegisterForDispose(fileContent);
fileContent.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("image/jpeg");
content.Add(fileContent);
});
var response = new HttpResponseMessage();
response.Content = content;
return response;
}
The response's Content-Type header shows as Content-Type: multipart/mixed; boundary="7aeff3b4-2e97-41b2-b06f-29a8c23a7aa7" and each file is packed in different blocks separated by the boundary.
Client:
The client application is a .Net Core 3.0.1 console application.
Please note the synchronous usage of the async methods. This can be easily changed to asynchronous using await, but implemented like this for simplicity:
using System;
using System.IO;
using System.Net.Http;
namespace console
{
class Program
{
static void Main(string[] args)
{
using (HttpClient httpClient = new HttpClient())
{
using (HttpResponseMessage httpResponseMessage = httpClient.GetAsync("http://localhost:60604/api/GetImage/GetFiles").Result)
{
var content = (HttpContent)new StreamContent(httpResponseMessage.Content.ReadAsStreamAsync().Result);
content.Headers.ContentType = httpResponseMessage.Content.Headers.ContentType;
MultipartMemoryStreamProvider multipartResponse = new MultipartMemoryStreamProvider();
content.ReadAsMultipartAsync(multipartResponse);
for(int i = 0; i< multipartResponse.Contents.Count;i++)
{
Stream contentStream = multipartResponse.Contents[i].ReadAsStreamAsync().Result;
Console.WriteLine("Content {0}, length {1}", i, contentStream.Length);
}
}
}
}
}
}
I'm using .net core to upload and retrieve an image from a private Amazon S3 bucket.
I'm able to upload it successfully, and even view it after I download it from S3, however when I'm a bit unsure about how to return the stream/response back to the client for the actual API call (for example right now I'm just trying to use Postman/Fiddler proxy tools to get back the image from my API)
My code for S3 to retrieve the stream:
///Retrieve my image from my bucket
public async Task<string> ReadObjectData(MediaFolder key, String fileName)
{
string responseBody = "";
IAmazonS3 client;
using (client = new AmazonS3Client(accessKey, accessSecret, endpoint))
{
Amazon.S3.Model.GetObjectRequest request = new Amazon.S3.Model.GetObjectRequest
{
BucketName = bucket,
Key = key + "/" + fileName,
};
using (GetObjectResponse response = await client.GetObjectAsync(request))
using (Stream responseStream = response.ResponseStream)
using (StreamReader reader = new StreamReader(responseStream))
{
string title = response.Metadata["x-amz-meta-title"];
responseBody = reader.ReadToEnd();
}
}
return responseBody;
}
So now in my controller, I have the following action:
[HttpGet("ProfilePic")]
public async Task<IActionResult> GetProfilePicture()
{
var user = await GetUserFromBearerToken();
//Retrieve
var utf8ImageResponse = await _fileService.ReadObjectData(MediaFolder.Profiles, user.ProfileImageFileName);
//To return a file as a stream
var imageBytes = System.Text.Encoding.UTF8.GetBytes(utf8ImageResponse);
//Return the image, which I'll hardcode as jpeg for a test
return File(imageBytes, "image/jpeg");
}
When I make the call using Postman, it returns a little blank box (the box you'd see if you tried to return an image, but it wasn't a valid image or null in some way).
Right now I'm using Postman but ideally I'd want an app to present this image.
Any ideas what I'm doing wrong? I tried messing around with base64 encoding and other things but nothing seems to work.
Thanks!
This way you can retrieve the file as stream from S3 storage
public async Task<Stream> ReadObjectData(MediaFolder key, String fileName)
{
try
{
using (var client = new AmazonS3Client(accessKey, accessSecret, endpoint))
{
var request = new GetObjectRequest
{
BucketName = bucket,
Key = key + "/" + fileName
};
using (var getObjectResponse = await client.GetObjectAsync(request))
{
using (var responseStream = getObjectResponse.ResponseStream)
{
var stream = new MemoryStream();
await responseStream.CopyToAsync(stream);
stream.Position = 0;
return stream;
}
}
}
}
catch (Exception exception)
{
throw new Exception("Read object operation failed.", exception);
}
}
And then - return this stream as FileStreamResult:
[HttpGet("ProfilePic")]
public async Task<IActionResult> GetProfilePicture()
{
var user = await GetUserFromBearerToken();
Stream imageStream = await _fileService.ReadObjectData(MediaFolder.Profiles, user.ProfileImageFileName);
Response.Headers.Add("Content-Disposition", new ContentDisposition
{
FileName = "Image.jpg",
Inline = true // false = prompt the user for downloading; true = browser to try to show the file inline
}.ToString());
return File(imageStream, "image/jpeg");
}
I need create POST request from WinRT app,which should contain StorageFile.
I need to do this exactly in style like this : post request with file inside body.
Is it possible? I know about HttpClient.PostAsync(..), but I can't put StorageFile inside request body. I want to send mp3 file to Web Api
On server side I get file like this:
[System.Web.Http.HttpPost]
public HttpResponseMessage UploadRecord([FromUri]string filename)
{
HttpResponseMessage result = null;
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var filePath = HttpContext.Current.Server.MapPath("~/Audio/" + filename + ".mp3");
postedFile.SaveAs(filePath);
}
result = Request.CreateResponse(HttpStatusCode.Created);
}
else
{
result = Request.CreateResponse(HttpStatusCode.BadRequest);
}
return result;
}
You can send it as a byte[] using the ByteArrayContent class as a second parameter:
StroageFile file = // Get file here..
byte[] fileBytes = null;
using (IRandomAccessStreamWithContentType stream = await file.OpenReadAsync())
{
fileBytes = new byte[stream.Size];
using (DataReader reader = new DataReader(stream))
{
await reader.LoadAsync((uint)stream.Size);
reader.ReadBytes(fileBytes);
}
}
var httpClient = new HttpClient();
var byteArrayContent = new ByteArrayContent(fileBytes);
await httpClient.PostAsync(address, fileBytes);
If you're uploading files of any appreciable size, then it's best to use the Background Transfer API so that the upload doesn't get paused if the app is suspended. Specifically see BackgroundUploader.CreateUpload which takes a StorageFile directly. Refer to the Background Transfer sample for both the client and server sides of this relationship, as the sample also includes a sample server.
To use less memory you can pipe the file stream to the HttpClient stream directly.
public async Task UploadBinaryAsync(Uri uri)
{
var openPicker = new FileOpenPicker();
StorageFile file = await openPicker.PickSingleFileAsync();
if (file == null)
return;
using (IRandomAccessStreamWithContentType fileStream = await file.OpenReadAsync())
using (var client = new HttpClient())
{
try
{
var content = new HttpStreamContent(fileStream);
content.Headers.ContentType =
new HttpMediaTypeHeaderValue("application/octet-stream");
HttpResponseMessage response = await client.PostAsync(uri, content);
_ = response.EnsureSuccessStatusCode();
}
catch (Exception ex)
{
// Handle exceptions appropriately
}
}
}
Problem:
I have a Java spring rest service to upload a file (large size).
I want use a .NET httpClient (or other .net client) to call upload service.
Questions:
It seems that best option to send large file is multi-part file, what's about interoperability ?
If it weren't possible, what is the best alternative ?
Thank you!
This is the answer:
I can send a file with multipart attachment from c# client to Java JAX Rest Webservice.
try
{
using (
var client = new HttpClient())
using (var form = new MultipartFormDataContent())
{
using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
using (var fileContent = new StreamContent(stream)) {
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {FileName = fileName, DispositionType = DispositionTypeNames.Attachment, Name = "fileData"};
form.Add(fileContent);
// only for test purposes, for stable environment, use ApiRequest class.
response = client.PostAsync(url, form).Result;
}
}
}
return response.RequestMessage != null ? response.ReasonPhrase : null;
}
catch (Exception ex)
{
TraceManager.TraceError("Post Asyn Request to " + url + " \n" + ex.Message, ex);
throw;
}
HTTP is a standard that is independent of OS platforms and programming languages, so you shouldn't have any problems with interoperability in case your .net client complies with the standards.
java spring boot
#RequestMapping(value="/upload", method=RequestMethod.POST)
public #ResponseBody String upload(#RequestParam("FileParam") MultipartFile file){
InputStream fromClient=file.getInputStream();
...do stuff with the database/ process the input file...
c#
HttpClient client = new HttpClient();
MultipartFormDataContent form = new MultipartFormDataContent();
FileInfo file = new FileInfo(#"<file path>");
form.Add(new StreamContent(file.OpenRead()),"FileParam",file.Name);
HttpResponseMessage response = await client.PostAsync("http://<host>:<port>/upload", form);
Console.WriteLine(response.StatusCode);
Console.WriteLine(response.ReasonPhrase);
Console.WriteLine(response.ToString());
Console.WriteLine(Encoding.ASCII.GetString(await response.Content.ReadAsByteArrayAsync()));