download xlsx as httpresponsemessage - c#

I am trying to download a xlsx extension file to be encaspulated as a httpresponsemessage. The file doesn't appear for downloading, however the XHR Requests in chrome seems to contain the data.
public HttpResponseMessage GetExcelFile(string csvdata)
{
var result = new HttpResponseMessage(HttpStatusCode.OK);
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
MemoryStream memStream = new MemoryStream(encoding.GetBytes(csvdata));
result.Content = new ByteArrayContent(encoding.GetBytes(csvdata));
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "Data.xlsx";
return result;
}
This is the api call that throws the httpresponsemessage into angularjs UI.
Any suggestions to get this to work?.

Hoping that you are using web API get method to return HttpResponseMessage in ValuesController.cs class. ( // GET: api/Values)
assume that ng-click fires at your button click,
add ng-click="TakeIT()" as attribute in button element.
in your angular controller, bind the function to $scope as,
$scope.TakeIT= function () {
window.location = 'api/Values';
};

Related

Pass files to another asp.net core webapi from another webapi

Existed asp.net core Api is look like below
public async Task<IActionResult> UploadAsync()
{
IFormFile file = null;
var files = Request.Form.Files;
if (files.Count > 0)
{
file = Request.Form.Files[0];
var fileText = new StringBuilder();
using (var reader = new StreamReader(file.OpenReadStream()))
{
while (reader.Peek() >= 0)
fileText.AppendLine(reader.ReadLine());
}
int stagingDetailId = await _stagingMarketProvider.GetV1StagingStatusDetailId();
var result = await SaveStagingMarketsAsync(_fileProvider.ReadImportedMarkets(fileText.ToString()));
return Ok(result);
}
return Ok();
}
Now to consume that api from another asp.net core webapi, I have to pass those files through Request object only, I can't change any existed Api code because of business.
Solution 1: Applicable if you want your client to get redirected to other API
Assuming the API caller understands HTTP 302 and can act accordingly, the 302 redirect should help you.
public IActionResult Post()
{
return Redirect("http://file-handler-api/action");
}
From documentation, Redirect method returns 302 or 301 response to client.
Solution 2: C# Code To Post a File Using HttpClient
Below c# code is from this blog post. This is simple code which creates HttpClient object and tries to send the file to a web API.
As you are doing this from one API to another, you will have to save file first at temporary location. That temporary location will be parameter to this method.
Also, After upload you may want to delete the file if it is not required. This private method you can call after file upload to your first API is complete.
private async Task<string> UploadFile(string filePath)
{
_logger.LogInformation($"Uploading a text file [{filePath}].");
if (string.IsNullOrWhiteSpace(filePath))
{
throw new ArgumentNullException(nameof(filePath));
}
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"File [{filePath}] not found.");
}
using var form = new MultipartFormDataContent();
using var fileContent = new ByteArrayContent(await File.ReadAllBytesAsync(filePath));
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
form.Add(fileContent, "file", Path.GetFileName(filePath));
form.Add(new StringContent("789"), "userId");
form.Add(new StringContent("some comments"), "comment");
form.Add(new StringContent("true"), "isPrimary");
var response = await _httpClient.PostAsync($"{_url}/api/files", form);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<FileUploadResult>(responseContent);
_logger.LogInformation("Uploading is complete.");
return result.Guid;
}
Hope this helps you.

How to pass a pdf from WebAPI and read the pdf from the MVC Controller?

I have a Web API service that should return a PDF.
I am then trying to call that WebAPI method to read the PDF.
Here is my API Method:
[HttpPost]
[Route("GetTestPDF")]
public HttpResponseMessage TestPDF()
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(new FileStream(#"C:\MyPath\MyFile.pdf", FileMode.Open, FileAccess.Read));
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = "MyFile.pdf";
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
return Request.CreateResponse(HttpStatusCode.OK, response);
}
However when I go to read the response I don't see the pdf contents. I am not sure where I am going wrong with this.
Controller Method:
public ActionResult GetPDF()
{
var response = new HttpResponseMessage();
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(#"my local host");
response = httpClient.PostAsync(#"api/job/GetTestPDF", new StringContent(string.Empty)).Result;
}
var whatisThis = response.Content.ReadAsStringAsync().Result;
return new FileContentResult(Convert.FromBase64String(response.Content.ReadAsStringAsync().Result), "application/pdf");
}
When I examine whatisThis variable I see the content type and the content dispostion correctly set from my API. However I don't see the content of the PDF.
How can I read the PDF content?
EDIT:
If I read the content as a string in my MVC site I see. (I don't see the actual content of the file)
{"Version":{"_Major":1,"_Minor":1,"_Build":-1,"_Revision":-1},"Content":{"Headers":[{"Key":"Content-Disposition","Value":["attachment; filename=MyFile.pdf"]},{"Key":"Content-Type","Value":["application/pdf"]}]},"StatusCode":200,"ReasonPhrase":"OK","Headers":[],"RequestMessage":null,"IsSuccessStatusCode":true}
I stepped through the WebAPI and it is successfully reading and setting the response.Content with the file contents.
Still not sure if this is an issue on the WebAPI side or the MVC side.
I'll post this initially as an answer because it's easier to format code!
I made an API endpoint to return a PDF file, and if I call it from a browser the file opens as expected.
As your API doesn't appear to do this, let's assume that the problem is there, and hence this.
Here is the endpoint code, that is very similar to yours, but missing the ContentDisposition stuff:
public HttpResponseMessage Get()
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
FileStream fileStream = File.OpenRead("FileName.pdf");
response.Content = new StreamContent(fileStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
}

Prevent IDM from downloading automatically in web api

I have a web api method that returns an HttpResponseMessage containing a PDF file. The method looks something like this:
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StreamContent(new FileStream(path, FileMode.Open, FileAccess.Read));
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = fileName;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
When I call this api from client (which is written in angularJS), the Internet Download Manager automatically catches the PDF file and wants to download it. And because I have a security plan for my project, the IDM automatically requests username and password.
Does anyone have an idea about how I'm supposed to programmatically stop IDM from catching the PDF file?
Update: Here's my angularJS code:
$http.post(url, { transactionId: txId }
, {responseType: 'arraybuffer'})
.success(function (response) {
var reader = new FileReader();
var file = new Blob([response.data], {type: 'application/pdf'});
reader.onload = function (e) {
var printElem = angular.element('#printPdfLink');
printElem.attr('target', '_blank');
printElem.attr('href', reader.result);
printElem.attr('ng-click', '');
};
reader.readAsDataURL(file);
})
.error(function (error) {});
Change the mime type to application/octet-stream as a way to work around your problem. Make sure that the file name includes a proper file extension so that it can be recognized by the client system once downloaded.
Another issue is the attachment disposition of the content which typically forces it to save it as a file download. Change it to inline so that the client can consume it without IDM trying to download it as an attachment.
FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
StreamContent content new StreamContent(stream);
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline");
content.Headers.ContentDisposition.FileName = fileName;
content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = content;
return response;
I have try to use HttpResponseMessage.
If I use ContentDisposition is inline then response break the file. If use attachment then IDM can detect it.
At the end of the day, I found Accept-Ranges header can make download without IDM but it not valid in HttpResponseMessage.
You can try out my code below to make download file without IDM:
[HttpGet]
[Route("~/download/{filename}")]
public void Download(string filename)
{
// TODO lookup file path by {filename}
// If you want to have "." in {filename} you need enable in webconfig
string filePath = "<path>"; // your file path here
byte[] fileBytes = File.ReadAllBytes(filePath);
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("Accept-Ranges", "bytes");
HttpContext.Current.Response.ContentType = "application/octet-stream";
HttpContext.Current.Response.AddHeader("ContentDisposition", "attachment, filename=" + filename);
HttpContext.Current.Response.BinaryWrite(fileBytes);
HttpContext.Current.Response.End();
}
Note: filename parameter serve for download file name so you can config in webconfig if you want to have file extension (disabled by default).

Asp.net Web API return File with metadata about the file

I have a web api method that downloads a file:
public HttpResponseMessage DownloadDocument()
{
XDocument xDoc = GetXMLDocument();
MemoryStream ms = new MemoryStream();
xDoc.Save(ms);
ms.Position = 0;
var response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StreamContent(ms),
};
// Sending metadata
// Is this the right way of sending metadata about the downloaded document?
response.Content.Headers.Add("DocumentName", "statistics.xml");
response.Content.Headers.Add("Publisher", "Bill John");
return response;
}
Is this the correct way of sending metadata about the StreamContent i return? or should i return a different type of Content?
For the filename you'd better use the Content-Disposition response header which is specifically designed for this purpose. As far as the Publisher is concerned, you could indeed use a custom HTTP header (as you did) or simply include it as some sort of metadata tag directly inside the payload. For example:
public HttpResponseMessage Get()
{
XDocument xDoc = GetXMLDocument();
var response = this.Request.CreateResponse(
HttpStatusCode.OK,
xDoc.ToString(),
this.Configuration.Formatters.XmlFormatter
);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "statistics.xml"
};
response.Headers.Add("Publisher", "Bill John");
return response;
}

ASP .net Web Api downloading and viewing images

I want to download images from a server and display them in the browser. But when I enter the url in the browser (localhost:port/api/service/imageID), the download box appears asking me wether to save or open the image. But I want the image to be straight displayed in the browser.
This is my controller 'Get' method:
public HttpResponseMessage Get(int id)
{
HttpResponseMessage response;
var image = _repository.RetrieveImage(id);
if (image == null)
{
response = new HttpResponseMessage(HttpStatusCode.NotFound);
}
else
{
response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(new MemoryStream(image.ImageData));
response.Content = new ByteArrayContent(image.ImageData);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = image.OriginalFileName;
response.Content.Headers.ContentType = new MediaTypeHeaderValue(image.Mime);
response.Content.Headers.ContentLength = image.ImageData.Length;
}
return response;
Thanks a lot for any help
Don't use the "attachment" content disposition header. Using that header instructs browsers to download the specified file instead of showing it inline.
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
For completeness, note that removing the Content-Disposition will also remove any hint of the file's name when saved using "Save As" context menu, and a filename will be suggested based on the URL, which in this case will be something like "42.jpg", because the last part of the URL is an ID. If you want to preserve the file name during save, change the Content-Disposition to "inline":
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline")
{
FileName = image.OriginalFileName,
Size = image.ImageData.Length
};
For you scenario, i think you could just return a StreamContent and provide the appropriate content-type header of this content. (ex: image/jpeg)

Categories