C# How To Download File From Server-Side - c#

im going to lose my mind and i'm a newbie. I've two controller which one is from Server-Side another one is called by jquery ajax Client-Side. I've a folder with excel files on Server-Side. I'm trying to get this folder from server-side and download from client-side to user's download folder or with any save-as dialog.
Here is my Server-side Controller
[HttpGet]
[Route("DownloadExcelFile")]
public IActionResult DownloadExcelFile(string fileName)
{
try
{
return File(
fileContents: _wholesaleService.DownloadExcelFile(fileName),
contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
fileDownloadName: fileName
);
}
catch (Exception ex)
{
return default;
}
}
and my fileContents is a byte array which is like below
public byte[] DownloadExcelFile(string fileName)
{
string sourcePath = GetFolderPath() + fileName;
var bytes = File.ReadAllBytesAsync(sourcePath);
return bytes.Result;
}
So this is my server-side. And my client-side controller is
[HttpGet]
public async Task<IActionResult> DownloadFile(string fileName)
{
var response = await _httpHelper.GetFormDataAsync<IActionResult>($"WholesaleManagement/DownloadExcelFile?filename={fileName}");
return StatusCode(200,response);
}
This controller is triggered by an ajax call and it directly goes to server-side controller with httphelper class which is like below
public async Task<HttpResponseMessage> GetFormDataAsync<T>(string methodName, HttpStatusCode successStatusCode = HttpStatusCode.OK) where T : class
{
if (!urlWhiteList.Contains(methodName))
throw new HttpRequestException(methodName);
using (HttpClient httpClient = new HttpClient ())
{
var requestUri = QueryHelpers.AddQueryString(methodName, "", "");
var response = await httpClient.GetAsync(requestUri);
return response;
}
}
After all that my use case is : User will click to filename from website and i will take that filename to my controller and download this file to user's computer with a pop-up. I hope it is clear and i can give much more details if you want to. How to achieve this?

Try this one. I spent more times before. Hope this help.
var httpRequest = HttpContext.Current.Request;
var filePath = string.Empty;
HttpPostedFile postedFile = null;
foreach (string file in httpRequest.Files)
{
postedFile = httpRequest.Files[file];
filePath = HttpContext.Current.Server.MapPath("~/TempFile/" + postedFile.FileName);
postedFile.SaveAs(filePath);
}

Related

Sending a file with RestClient request

I have a service in .Net Core which takes an IFormFile as a parameter. I would like to send the data from this IFormFile up to an Api. The code below is the auto generated code from postman which worked for my request. In the 'attachmentRequest.AddFile("file", file);' line, file is a string of the local path which postman uploaded the file from. What is the best way to send my IFormfile as the file sent? Do I use a stream and save the IFormfile to a location in .Net Core and pass that address?
public async Task<bool> PostIssue(IFormFile file)
{
var client = new RestClient("https://sample/10000/attachments");
client.Timeout = -1;
var attachmentRequest = new RestRequest(Method.POST);
attachmentRequest.AddHeader("X-Atlassian-Token", "no-check");
attachmentRequest.AddHeader("Authorization", "Basic xxxxxxxxxxxxxx=");
attachmentRequest.AddHeader("Cookie", "atlassian.xsrf.token=xxxxxxxxxxxxxxxxxx_lin");
attachmentRequest.AddFile("file", file);
IRestResponse attachmentResponse = client.Execute(attachmentRequest);
Console.WriteLine(attachmentResponse.Content);
if (attachmentResponse.IsSuccessful)
{
return true;
}
else
{
return false;
}
}
You use the second method of AddFile:
public IRestRequest AddFile (string name, byte [] bytes, string fileName, string contentType = null);
Convert the IFormFile file to byte array format instead of getting the path to transfer the file.
What you said is that uploading to the local using stream is indeed possible, but this is an extra action, you can directly operate IFormFile into byte form and transfer it to the API, as shown below:
public async Task<bool> PostIssue(IFormFile file)
{
var client = new RestClient("http://localhost:50855/Users");
client.Timeout = -1;
var attachmentRequest = new RestRequest(Method.POST);
attachmentRequest.AddHeader("X-Atlassian-Token", "no-check");
attachmentRequest.AddHeader("Authorization", "Basic xxxxxxxxxxxxxx=");
attachmentRequest.AddHeader("Cookie", "atlassian.xsrf.token=xxxxxxxxxxxxxxxxxx_lin");
using (var ms = new MemoryStream())
{
file.CopyTo(ms);
var fileBytes = ms.ToArray();
attachmentRequest.AddFile("filename",fileBytes, file.FileName);
}
IRestResponse attachmentResponse = client.Execute(attachmentRequest);
Console.WriteLine(attachmentResponse.Content);
if (attachmentResponse.IsSuccessful)
{
return true;
}
else
{
return false;
}
}
Api:
public async Task<IActionResult> attachments()
{
var file = Request.Form.Files["filename"]; //get the file
//do something you want
return Ok();
}
Here is the test result:

Unable to File Upload From Angular 8 to Asp.net Core 2.2

I have asp.net core server (using .net core 2.2) that has FileUploadController which listens to post request for incoming file.
[HttpPost("Upload")]
// public async Task<IActionResult> Upload([FromForm(Name="file")]IFormFile file) {
// public async Task<IActionResult> Upload([FromForm]IFormFile file) {
public async Task<IActionResult> Upload(IFormFile file) {
Console.WriteLine("***" + file);
if(file == null) return BadRequest("NULL FILE");
if(file.Length == 0) return BadRequest("Empty File");
Console.WriteLine("***" + host.WebRootPath);
if (string.IsNullOrWhiteSpace(host.WebRootPath))
{
host.WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
}
var uploadsFolderPath = Path.Combine(host.WebRootPath, "uploads");
if (!Directory.Exists(uploadsFolderPath)) Directory.CreateDirectory(uploadsFolderPath);
var fileName = "Master" + Path.GetExtension(file.FileName);
var filePath = Path.Combine(uploadsFolderPath, fileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return Ok("Okay");
}
I have created angular app (using angular version 8) which can choose file to upload at ClientApplication and I have created three post service that called to the api "http://localhost:5000/api/fileupload/upload".
Standard Angular HttpClient Post. when server reads, the IFormFile is null.
const formData: FormData = new FormData();
formData.append('file', file, file.name);
// return this.http.post(this.endpoint, file);
return this.http.post(this.endpoint, formData); // Problem solved
Added HttpHeaders, I try empty headers, undefined and other propose solution from stackoverflow and google.
const header = new HttpHeaders() //1
header.append('enctype', 'multipart/form-data'); //2
header.append('Content-Type', 'multipart/form-data'); //3
If I put httpheader with resources in request, server gives 415 (unsupported media type)
I try HttpRequest from '#angular/common/http' which finally give me result that i want.
const formData: FormData = new FormData();
formData.append('file', file, file.name);
const req = new HttpRequest('POST', this.endpoint, formData);
return this.http.request(req);
I want to know is it a bug or my misunderstanding? If you check online tutorial, most of the developers use "this.HttpClient.post".
From what I read, I can use httpclient.post and angular framework will auto set the proper header for user. It seems like it's not doing the job.
After thorough investigation, first error is my mistake of using file
instead of formData, second error is header "content-type" declare in
httpinterceptor which after removed, it load the file as expected.
#Injectable()
export class JwtInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// add authorization header with jwt token if available
// if (request.url.indexOf('/upload')) {
// return next.handle(request);
// }
const token = localStorage.getItem('token');
const currentUser = JSON.parse(localStorage.getItem('user'));
if (currentUser && token) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
// 'Content-Type': 'application/json' <---- Main Problem.
}
});
}
return next.handle(request).pipe(catchError(err => this.handleError(err)));
}
}
Server: "https://github.com/phonemyatt/TestPlaygroundServer"
Client: "https://github.com/phonemyatt/TestPlayground"
In your first example that doesn't work, you are passing file into post(...) instead of formData. It should be:
const formData: FormData = new FormData();
formData.append('file', file, file.name);
return this.http.post(this.endpoint, formData);
The code you show for the controller appears to be correct, so this should be the only change that's required. You do not need to set any custom headers on the request that is sent from Angular.
Below code work for you
uploadSecond(file: File) {
const formData: FormData = new FormData();
formData.append('file', file, file.name);
return this.http.post('https://localhost:44393/api/fileupload/UploadSecond', formData);
}
Then in your controller
[HttpPost("UploadSecond")]
[DisableRequestSizeLimit]
public async Task<IActionResult> UploadSecond([FromForm]IFormFile file)
If you are using FormData in client you can get files like this.
[HttpPost("Upload"), DisableRequestSizeLimit]
public ActionResult Upload()
{
try
{
var file = Request.Form.Files[0];
var folderName = Path.Combine("Resources","Images");
var pathToSave = Path.Combine(Directory.GetCurrentDirectory(), folderName);
if (file.Length > 0)
{
var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
var fullPath = Path.Combine(pathToSave, fileName);
var dbPath = Path.Combine(folderName, fileName);
using (var stream = new FileStream(fullPath, FileMode.Create))
{
file.CopyTo(stream);
}
return Ok(new { dbPath });
}
else
{
return BadRequest();
}
}
catch (Exception ex)
{
return StatusCode(500, "Internal server error");
}
}

Accepting audio file in api

I am trying to convert my API to accept an audio file instead of a string but after looking into it I can not find a suitable example on how to do so.
At the moment the Speech-To-Text service is run locally but I want to move this to the server. The API call to the wit.ai services I already made. The thing left is to make the API accept a audio file(audio will always be .wav)
If anyone has some suggestions let me know I am stuck on this
[Produces("application/json")]
[Route("api")]
public class CommandApiController : Controller
{
// constructor
[HttpPost]
public async Task<IActionResult> ProcessCommandAsync([FromBody]string command)
{
// Testing SpeechToText method
string path = #"C:\Users\rickk\Desktop\SmartSpeaker\agenda.wav";
// Overridden for now
command = await CovnvertSpeechToTextApiCall(new ByteArrayContent(System.IO.File.ReadAllBytes(path)));
// Logic
}
async Task<string> CovnvertSpeechToTextApiCall(ByteArrayContent content)
{
var httpClient = new HttpClient();
content.Headers.ContentType = MediaTypeHeaderValue.Parse("audio/wav");
// Wit.ai server token
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
var httpResponseMessage = await httpClient.PostAsync("https://api.wit.ai/speech", content);
if (httpResponseMessage.IsSuccessStatusCode)
{
var response = await httpResponseMessage.Content.ReadAsStringAsync();
var modeldata = Newtonsoft.Json.JsonConvert.DeserializeObject<Model.DeserializedJsonDataModel>(response);
return modeldata._text;
}
else
{
return null;
}
}
}
You can easily upload a file into Asp.net core web api using IFormFile as following where you accept a parameter of the previous type inside your post action
[HttpPost]
public async Task<IActionResult> UploadAudioFile(IFormFile file)
{
/*
* the content types of Wav are many
* audio/wave
* audio/wav
* audio/x-wav
* audio/x-pn-wav
* see "https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types"
*/
if (file.ContentType != "audio/wave")
{
return BadRequest("Wrong file type");
}
var uploads = Path.Combine(HostingEnvironment.WebRootPath, "uploads");//uploads where you want to save data inside wwwroot
var filePath = Path.Combine(uploads, file.FileName);
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
return Ok("File uploaded successfully");
}
You will need to ask for IHostingEnvironment object in your controller constructor using dependency injection as following:
public FileController(IHostingEnvironment hostingEnvironment)
{
HostingEnvironment = hostingEnvironment;
}
And then assign it to property inside your controller.
Beside that don't forget to send your request from the client as Multipart form as following (example):
Thanks for the comment.
Not exactly what I was looking for but that is my fault for not explaining it correctly. Here is what I have found as a solution.
[Produces("application/json")]
[Route("api/audio")]
[HttpPost]
public async Task<IActionResult> ProcessCommandAsync([FromForm]IFormFile command)
{
if(command.ContentType != "audio/wav" && command.ContentType != "audio/wave" || command.Length < 1)
{
return BadRequest();
}
var text = await CovnvertSpeechToTextApiCall(ConvertToByteArrayContent(command));
return Ok(FormulateResponse(text));
}
private ByteArrayContent ConvertToByteArrayContent(IFormFile audofile)
{
byte[] data;
using (var br = new BinaryReader(audofile.OpenReadStream()))
{
data = br.ReadBytes((int) audofile.OpenReadStream().Length);
}
return new ByteArrayContent(data);
}

Post files from ASP.NET Core web api to another ASP.NET Core web api

We are building a web application that consist of an Angular2 frontend, a ASP.NET Core web api public backend, and a ASP.NET Core web api private backend.
Uploading files from Angular2 to the public backend works. But we would prefer to post them forward to the private backend.
Current working code
[HttpPost]
public StatusCodeResult Post(IFormFile file)
{
...
}
From there I can save the file to disk using file.CopyTo(fileStream);
However, I want to re-send that file, or those files, or, ideally, the whole request to my second web api core.
I am not sure how to achieve this with the HttpClient class of asp.net core.
I've tried all kinds of things such as
StreamContent ss = new StreamContent(HttpContext.Request.Body);
var result = client.PostAsync("api/Values", ss).Result;
But my second backend gets an empty IFormFile.
I have a feeling it is possible to send the file(s) as a stream and reconstruct them on the other side, but can't get it to work.
The solution must use two web api core.
Solution
Public backend in DMZ
[HttpPost]
public StatusCodeResult Post(IFormFile file)
{
try
{
if (file != null && file.Length > 0)
{
using (var client = new HttpClient())
{
try
{
client.BaseAddress = new Uri(currentPrivateBackendAddress);
byte[] data;
using (var br = new BinaryReader(file.OpenReadStream()))
data = br.ReadBytes((int)file.OpenReadStream().Length);
ByteArrayContent bytes = new ByteArrayContent(data);
MultipartFormDataContent multiContent = new MultipartFormDataContent();
multiContent.Add(bytes, "file", file.FileName);
var result = client.PostAsync("api/Values", multiContent).Result;
return StatusCode((int)result.StatusCode); //201 Created the request has been fulfilled, resulting in the creation of a new resource.
}
catch (Exception)
{
return StatusCode(500); // 500 is generic server error
}
}
}
return StatusCode(400); // 400 is bad request
}
catch (Exception)
{
return StatusCode(500); // 500 is generic server error
}
}
Private backend
[HttpPost]
public void Post()
{
//Stream bodyStream = HttpContext.Request.Body;
if (Request.HasFormContentType)
{
var form = Request.Form;
foreach (var formFile in form.Files)
{
var targetDirectory = Path.Combine(_appEnvironment.WebRootPath, "uploads");
var fileName = GetFileName(formFile);
var savePath = Path.Combine(targetDirectory, fileName);
using (var fileStream = new FileStream(savePath, FileMode.Create))
{
formFile.CopyTo(fileStream);
}
}
}
}
Hi i had the same issue and this is what worked for me :
My setup is netCore MVC netCoreApi.
My MVC Controller looks like :
[HttpPost("UploadFiles")]
public async Task<IActionResult> Post(List<IFormFile> files)
{
Sp4RestClient dataPovider = new Sp4RestClient("http://localhost:60077/");
long size = files.Sum(f => f.Length);
foreach (var file in files)
{
await dataPovider.ImportFile(file);
}
return Ok();
}
DataProvider Method :
public async Task ImportFile(IFormFile file)
{
RestClient restClient = new RestClient(_queryBulder.BuildImportFileRequest());
using (var content = new MultipartFormDataContent())
{
content.Add(new StreamContent(file.OpenReadStream())
{
Headers =
{
ContentLength = file.Length,
ContentType = new MediaTypeHeaderValue(file.ContentType)
}
}, "File", "FileImport");
var response = await restClient.Post<IFormFile>(content);
}
}
And least my WebApi Controller :
[HttpPost]
[Route("ImportData")]
public IActionResult Import(IFormFile file)
{
return Ok();
}
To see the complete code here is my RestClient Post method :
public async Task<RestResult<T>> Post<T>(HttpContent content)
{
using (HttpClient httpClient = new HttpClient())
{
HttpResponseMessage response = await httpClient.PostAsync(Endpoint, content);
if (response.StatusCode == HttpStatusCode.Created)
{
T result = JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
return new RestResult<T> { Result = result, ResultCode = HttpStatusCode.OK };
}
RestResult<T> nonOkResult =
new RestResult<T> { Result = default(T), ResultCode = response.StatusCode, Message = await response.Content.ReadAsStringAsync() };
return nonOkResult;
}
}
// Yeah i know im not getting HttpStatusCode.Created back ;)
happy coding ;)
API Code
[Route("api/upload/{id}")]
[HttpPost]
public async Task<IActionResult> Post(string id)
{
var filePath = #"D:\" + id; //+ Guid.NewGuid() + ".png";
if (Request.HasFormContentType)
{
var form = Request.Form;
foreach (var formFile in form.Files)
{
if (formFile.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
}
}
return Ok(new { Path = filePath });
}
Back End
[Route("home/UploadFile")]
[HttpPost]
public IActionResult UploadFile(IFormFile file)
{
if (file == null || file.Length == 0)
return Content("file not selected");
var client = new HttpClient();
byte[] data;
using (var br = new BinaryReader(file.OpenReadStream()))
data = br.ReadBytes((int)file.OpenReadStream().Length);
ByteArrayContent bytes = new ByteArrayContent(data);
MultipartFormDataContent multiContent = new MultipartFormDataContent
{
{ bytes, "file", file.FileName }
};
var result = client.PostAsync("http://localhost:2821/api/upload/" + file.FileName, multiContent).Result;
return RedirectToAction("file");
}
Download Source
I was in a similar situation - I needed a proxy method for forwarding not only files but also JSON data and whatnot. I did not want to do any analysis of the data in my proxy to let the final receiver deal with it.
So with some help from #Anton Tykhyy I came to the following working solution:
byte[] arr = null;
using (var mems = new MemoryStream())
{
// read entire body into memory first because it might be chunked with unknown length
await request.Body.CopyToAsync(mems);
await mems.FlushAsync(); // not sure if needed after CopyToAsync - better safe then sorry
arr = mems.ToArray();
}
msg.Content = new ByteArrayContent(arr);
msg.Content.Headers.ContentLength = arr.Length;
// keep content-type header "as is" to preserve multipart boundaries etc.
msg.Content.Headers.TryAddWithoutValidation("Content-Type", request.ContentType);
var response = await _httpClient.SendAsync(msg);
I tested it with complex request that contained multipart form data with JSON field and multiple attached files, and all the data reached my backend server without any issues.
Ignoring the HttpClient when you call the private backend API, can you reference the private Core API project from the public Core API project and call the controller directly from the Core API project? See the request is still null/empty. If the request comes out with a value then the issue is with the use of the HttpClient.
Ideally, you want to create a package library(kind of SDK) for your private Core API that you want to distribute to consuming clients. This acts like a wrapper/proxy. This way you can isolate the private backend system and you can troubleshoot it in isolation. So you public Core API project(which is the private backend client) can reference it as nuget package.

Download Binary from WebApi2 As File. Server Side

I've looked a couple of answers on here but still having an issue.
One system is saving a binary byte[] into SQL 2014 with Entity Framework 6.
I have a "name" of the record (not the file) but I want to serve up the binary data as a downloadable file via the webapi2. I have it somewhat working but on the browser it shows the ID as the file name and says can't download file. It is prompting to download but then it can't.
Right now for PoC I'm hard coding the mime type to word docs. What am I doing wrong and how should I refactor this to also provide a file name.
I'm saving the document using Office Apps, Task Pane:
https://msdn.microsoft.com/en-us/library/office/jj715284.aspx
FileType: "Compressed" = Returns the entire document (.pptx or .docx) in Office Open XML (OOXML) format as a byte array.
Office.context.document.getSelectedDataAsync(Office.CoercionType.Text,
function (result) {
if (result.status === Office.AsyncResultStatus.Succeeded) {
vm.data.data = result.value;
//I call a angularJs service which post..
//return $http.post('https://stapi.local:8443/api/activities', vm.data);
} else {
}
}
);
Then I try to let a user download the document with the following.
WebAPI2 Controller:
[HttpGet, Route("api/activityObjectFile/{id}/{name}")]
public HttpResponseMessage GetDataFile(int id)
{
var fileByte = _activityService.GetFile(id);
HttpResponseMessage response = new HttpResponseMessage { Content = new StreamContent(new MemoryStream(fileByte))};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/msword");
response.Content.Headers.ContentLength = fileByte.Length;
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment filename=test.docx");
return response;
}
Class Service Library:
public Byte[] GetFile(int id)
{
var existingActivityObjectFile = _repo.QueryAll<ActivityObject>().Where(a => a.Id == id).Select(a => a.BinaryData).First();
return existingActivityObjectFile;
}
Client Url:
Download
This should get you going for now:
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(new MemoryStream(fileByte))
};
response.Content
.Headers
.Add("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "test.docx"
};
return response;
As for suggestions for refactoring, you could use my method.
I have an implementation of the IHttpActionResult that I return, it looks like this:
public class DocumentAttachmentResult : IHttpActionResult {
private readonly string fileName;
private readonly string mimeType;
private readonly byte[] blob;
public DocumentAttachmentResult(string fileName, string mimeType, byte[] blob) {
this.fileName = fileName;
this.mimeType = mimeType;
this.blob = blob;
}
private HttpResponseMessage Execute() {
var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StreamContent(new MemoryStream(this.blob)) };
response.Content.Headers.Add("Content-Type", this.mimeType);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = this.fileName };
return response;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) {
return Task.FromResult(this.Execute());
}
}
And my controller action looks like this:
[HttpGet]
[Route("attachments/{id:guid}/download")]
public IHttpActionResult DownloadAttachment(Guid id) {
var attachment = this.repository.FindById(id);
if (attachment == null) {
return this.NotFound();
}
return new DocumentAttachmentResult(attachment.Name, attachment.FileType.MimeType, attachment.BinaryBlob);
}
I store the file name, mime type and binary in SQL server have it modeled as one entity called Attachment. The mime type and file is captured when I upload a file using another action on my WebApi controller.

Categories

Resources