I have a c# controller that returns a status file for the system. It prompts the user to open or download the file. The return type for the controller is actually System.Net.Http.HttpResponseMessage.
I set up the route like this: /api/logging/status/{reqID}
If I goto the route directly in my web browser, http://mysite//api/logging/status/12345678, it works fine.
How can I get the URL to the file via jquery so that I can put that url in an achor tag like:
Download Status
Here is the controller:
[Route("/api/logging/status/{reqID}")]
public IHttpActionResult GetStatus(Int32 reqID)
{
Stream stream;
stream = new MemoryStream();
var reqStatus = serializer.SerializeToString(req);
var bytes = System.Text.Encoding.UTF8.GetBytes(reqStatus);
stream.Write(bytes, 0, bytes.Length);
stream.Seek(0, SeekOrigin.Begin);
}
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.Add("Content-Disposition", String.Format("attachment; filename=\"{0}\"", "status.txt"));
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
return ResponseMessage(response);
}
Is there something I'm missing?
Thanks!
Have you already tried the following?
$.get('/api/logging/status/12345678', function (data) {
console.log(data);
});
#999cm999,
It seems to me that you need another service method to serve the path to the txt file. You only have a method that serves the file itself as a download. That's how the browser example works.
However, if you want to insert the path to the txt file (http://path.to.my.status.file/status.txt) in an anchor tag of your HTML page, the method that you have does not fit. Create another method that returns the path to the file as a string. Then you can grab that and put it in the HREF attribute of your hyperlink element using jQuery or your favorite JS approach.
Let me know of your findings.
Related
I'm currently in the process of migrating several Azure Function Apps to .NET 6. One of these involves returning various content files via a HTTP request.
Previously (on .NET 3.1) this works fine for both json/text files, and HTML:
var callbackFileLocation = Path.Combine(Helper.GetFunctionPath(), "Files", filename);
var stream = new FileStream(callbackFileLocation, FileMode.Open, FileAccess.Read)
{
Position = 0
};
var okObjectResult = new OkObjectResult(stream);
okObjectResult.ContentTypes.Clear();
if (filename.Contains(".html"))
{
okObjectResult.ContentTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("text/html"));
}
else
{
okObjectResult.ContentTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
}
return okObjectResult;
This doesn't return the same results on .NET Core 6 - you tend to just get given the object type as a name e.g. Microsoft.AspNetCore.Mvc.OkObjectResult or System.IO.FileStream. It's easy enough to fix for the json files, as I can just convert them into text, and make sure the function app is returning that as the payload.
HTML seems trickier - I've tried reading the stream to end, and various methods mentioned here and on other sites, e.g:
public static HttpResponseMessage Run(string filename)
{
var callbackFileLocation = Path.Combine(Helper.GetFunctionPath(), "Files", filename);
var response = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(callbackFileLocation, FileMode.Open);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
return response;
}
Or returning the HTML text within FileContentResult ("application/octet-stream") or ContentResult, e.g:
new ContentResult { Content = content, ContentType = "text/html", StatusCode = 200 };
The closest I've got is the HTML as raw text, but want the HTML rendered in the browser.
Any suggestions? Documentation around this on .NET 6 seems thin...thanks!
Not the best answer (and thanks for the help #Jonas Weinhardt) - but I couldn't find a way to do this using dotnet-isolated process.
It worked fine when moved back to non-isolated. (I guess it's something to do with the new GRPC functions or something like that?)
I am trying to download a simple xlsx file using web api but the file is always corrupt and I am yet to figure out why.I am using C# ClosedXML.Excel and following a basic example which can be found here:
ClosedXml examples
[HttpGet]
[Route("campaigns/{id}/contact-points/excel")]
[SwaggerResponse(491, "TokenInvalid")]
[SwaggerResponse(HttpStatusCode.NotFound)]
[SwaggerResponse(HttpStatusCode.Forbidden)]
[ResponseType(typeof(HttpResponseMessage))]
public async Task<IHttpActionResult> GetCampaignContactPointsExcel(int id, int frequency, string txtFrequency)
{
var wb = new XLWorkbook();
var ws1 = wb.Worksheets.Add("Sheet1");
ws1.Cell("A1").SetValue(1).AddToNamed("value1");
var ws2 = wb.Worksheets.Add("Sheet2");
ws2.Cell("A1").SetFormulaA1("=value1").AddToNamed("value2");
var responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
using (var memoryStream = new MemoryStream())
{
wb.SaveAs(memoryStream);
responseMessage.Content = new ByteArrayContent(memoryStream.ToArray());
responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
responseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "ContactPoints.xlsx"
};
memoryStream.Close();
}
return ResponseMessage(responseMessage);
}
I am also using swagger and when I click on the link it downloads the file and opens it as a xlsx file but it always says it's corrupt.
There is nothing wrong in the backend code. On the frontend side you need to set responseType='blob'
Seems like this is an issue related to swagger ui 2. Please refer to [https://github.com/swagger-api/swagger-ui/issues/1605][1].
You can try curl or Postman to see if it downloads the file correctly.
While calling the service set Response type as 'blob'
return this.http.get(this.url +'PA/Downloadexcel/'+revisionId,{ responseType :'blob'});
I have a project where I am using NPOI to generate Excel document from my Angular application. I have the calls being made from my angular service to my webapi controller as follows:
function exportReportToExcel(report) {
return $http.post('reportlibrary/exportReport/', report, {
}).then(function (response) {
return response.data;
});
};
Within the controller I make the following call
[HttpPost]
public HttpResponseMessage ExportReport([FromBody]DTOs.Report report)
{
try
{
IReportPersistenceManager manager = ContainerConfigurator.Instance.Resolve<IReportPersistenceManager>();
MemoryStream ms = new MemoryStream();
//we have to pass to the NOPI assemble file type as well as file name
//since we only deal with excel for now we will set it but this could be configured later.
long id = report.ReportId;
string mimeType = "application/vnd.ms-excel";
string filename = "unknown";
manager.ExportDataToExcel(id, (name, mime) =>
{
mimeType = mime;
filename = name;
return ms;
});
ms.Position = 0;
var response = new HttpResponseMessage();
response.Content = new ByteArrayContent(ms.ToArray());
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.ms-excel");
return (response);
}
catch (Exception)
{
//error
return new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
}
}
This is a migration from an MVC app and previously I was able to return the object using System.IO.File to return the object as well as close the stream.
I've never done this with Angular but from what I have read it appears I can drop the memorystream object into a byteArray and pass that back to the client.
If this is the correct approach how to I unravel this object once it comes back to the angular service and controller.
The goal here is to allow the user to download a previously saved report as an Excel file.
Am I on the right track? Is there a more efficient way to download a memory stream object? How can I pass this context to the browser?
thanks in advance
I had a similar problem. I solved that changing the endpoint to support GET and calling this method from a new tab.
So from your angular app you have to create a new tab pointing to the path that calls the method that generate your document. You can open a tab with the next code:
$window.open(path)
I think that in the next link you can have all the information that you need:
Download file from an ASP.NET Web API method using AngularJS
I hope that it helps.
I have a legacy Classic ASP application that serves as a repository for a bunch of files available for download. I would prefer to not rewrite the entire application at this time but instead want to pull out the file download logic into an ASP.NET Web API.
I have created the service and when I hit the service via my browser the download starts just fine with the hardcoded filename I have in the app and I am able to save. I am having trouble consuming the service from my asp app however. If I modify the code below to just return a JSON value, I am able to return the value to the asp app. So I at least know there is communication happening. Just not sure how to handle a file download.
HERE IS MY SERVICE LOGIC:
// GET api/values/5
public HttpResponseMessage Get(int id)
{
//return "value";
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
string filePath = "H:/Temp/Filename.exe";
MemoryStream memoryStream = new MemoryStream();
FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read);
byte[] bytes = new byte[file.Length];
file.Read(bytes, 0, (int)file.Length);
memoryStream.Write(bytes, 0, (int)file.Length);
file.Close();
httpResponseMessage.Content = new ByteArrayContent(memoryStream.ToArray());
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "Filename.exe" };
httpResponseMessage.StatusCode = HttpStatusCode.OK;
return httpResponseMessage;
}
HERE IS A SUBSET OF MY CLASSIC ASP LOGIC:
Response.Addheader "Content-Disposition", "attachment; filename=Filename.exe"
Response.ContentType = "application/octet-stream" 'EXE
Set HttpReq = Server.CreateObject("MSXML2.ServerXMLHTTP")
HttpReq.open "GET", "http://webservice/api/values/1", False
HttpReq.send
Response.BinaryWrite HttpReq.responsestream
What about something like:
Set HttpReq = Server.CreateObject("ADODB.Stream")
HttpReq.open "GET", "http://webservice/api/values/1", False
Response.BinaryWrite HttpReq.read
Hi and thanks for looking!
Background
I am using the Rotativa pdf tool to read a view (html) into a PDF. It works great, but it does not natively offer a way to save the PDF to a file system. Rather, it only returns the file to the user's browser as a result of the action.
Here is what that code looks like:
public ActionResult PrintQuote(FormCollection fc)
{
int revisionId = Int32.Parse(Request.QueryString["RevisionId"]);
var pdf = new ActionAsPdf(
"Quote",
new { revisionId = revisionId })
{
FileName = "Quote--" + revisionId.ToString() + ".pdf",
PageSize = Rotativa.Options.Size.Letter
};
return pdf;
}
This code is calling up another actionresult ("Quote"), converting it's view to a PDF, and then returning the PDF as a file download to the user.
Question
How do I intercept the file stream and save the PDF to my file system. It is perfect that the PDF is sent to the user, but my client also wants the PDF saved to the file system simultaneously.
Any ideas?
Thanks!
Matt
I have the same problem, here's my solution:
You need to basically make an HTTP request to your own URL and save the output as a binary file. Simple, no overload, helper classes, and bloated code.
You'll need this method:
// Returns the results of fetching the requested HTML page.
public static void SaveHttpResponseAsFile(string RequestUrl, string FilePath)
{
try
{
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(RequestUrl);
httpRequest.UserAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)";
httpRequest.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)httpRequest.GetResponse();
}
catch (System.Net.WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
response = (HttpWebResponse)ex.Response;
}
using (Stream responseStream = response.GetResponseStream())
{
Stream FinalStream = responseStream;
if (response.ContentEncoding.ToLower().Contains("gzip"))
FinalStream = new GZipStream(FinalStream, CompressionMode.Decompress);
else if (response.ContentEncoding.ToLower().Contains("deflate"))
FinalStream = new DeflateStream(FinalStream, CompressionMode.Decompress);
using (var fileStream = System.IO.File.Create(FilePath))
{
FinalStream.CopyTo(fileStream);
}
response.Close();
FinalStream.Close();
}
}
catch
{ }
}
Then inside your controller, you call it like this:
SaveHttpResponseAsFile("http://localhost:52515/Management/ViewPDFInvoice/" + ID.ToString(), "C:\\temp\\test.pdf");
And voilĂ ! The file is there on your file system and you can double click and open the PDF, or email it to your users, or whatever you need.
return new Rotativa.ActionAsPdf("ConvertIntoPdf")
{
FileName = "Test.pdf", PageSize = Rotativa.Options.Size.Letter
};
Take a look at the MVC pipeline diagram here:
http://www.simple-talk.com/content/file.ashx?file=6068
The method OnResultExecuted() is called after the ActionResult is rendered.
You can override this method or use an ActionFilter to apply and OnResultExecuted interceptor using an attribute.
Edit:
At the end of this forum thread you will find a reply which gives an example of an ActionFilter which reads (and changes) the response stream of an action. You can then copy the stream to a file, in addition to returning it to your client.
I successfully used Aaron's 'SaveHttpResponseAsFile' method, but I had to alter it, as the currently logged in user's credentials weren't applied (and so it was forwarding to MVC4's login url).
public static void SaveHttpResponseAsFile(System.Web.HttpRequestBase requestBase, string requestUrl, string saveFilePath)
{
try
{
*snip*
httpRequest.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
httpRequest.Headers.Add(HttpRequestHeader.Cookie, requestBase.Headers["Cookie"]);
*snip*</pre></code>
Then in your calling Controller method, simply add 'Request' into the SaveHttpResponseAsFile call.
You can also do it using Rotativa, which is actually quite easy.
Using Rotativa;
...
byte[] pdfByteArray = Rotativa.WkhtmltopdfDriver.ConvertHtml( "Rotativa", "-q", stringHtmlResult );
File.WriteAllBytes( outputPath, pdfByteArray );
I'm using this in a winforms app, to generate and save the PDFs from Razor Views we also use in our web apps.
I was able to get Eric Brown - Cal 's solution to work, but I needed a small tweak to prevent an error I was getting about the directory not being found.
(Also, looking at the Rotativa code, it looks like the -q switch is already being passed by default, so that might not be necessary, but I didn't change it.)
var bytes = Rotativa.WkhtmltopdfDriver.ConvertHtml(Server.MapPath(#"/Rotativa"), "-q", html);