MVC - FileContentResult sends corrupted pdf - asp.net-mvc

I have a simple action method that returns a PDF document, that gets shown in an <iframe> with an <embed> tag, and every few calls to this method will return a corrupted PDF. (I've determined its corrupted by using dev tools to save the response from the server)
Action Method:
public FileContentResult GetPdfReport(string Id)
{
Response.AppendHeader("Content-Disposition", "inline; filename=report.pdf");
var content = System.IO.File.ReadAllBytes(Server.MapPath("~/Reports/Testfile.pdf"));
System.IO.File.WriteAllBytes(Server.MapPath("~/Reports/debugReport.pdf"), content);
return File(content, "application/pdf");
}
View Content:
<embed id="widgetResponsePdf" src="#Url.Action("GetPdfReport", "WidgetResponse", new { Id = "123" })" type="application/pdf" onmouseout="mouseOutHandler();" />
The files TestFile.pdf and debugReport.pdf open just fine when I get a corrupted PDF, and there is no difference in the request and response header between the normal request/response and the corrupted request/response.
Is there some setting in IIS that I am missing that could be causing the inconsistent behavior between requests, or could this be caused solely by a network issue?

In our case, the IFrame has a src attribute that points to a partial view that loads the <embed> content, which then has a src attribute that points to the actual PDF file instead of a PartialView that returns a FileContentResult. The example below has been simplified from our actual implementation
Partial View
<iframe> <!-- iframe is actually loaded from another partial view, it is show here to simplify things -->
<embed
src='#Url.Content(Model.ReportFileName)'
type="application/pdf">
</embed>
</iframe>
Controller
public PartialView GetPdfReport(string Id)
{
var model = PDFResponseModel
{
ReportFileName = Server.MapPath("~/Reports/Testfile.pdf")
};
return PartialView(model);
}
Due to a site restriction for IE support, our users (intranet site) are required to have AdobeAcrobat installed to view PDF's. While this solution works for us, this may not be desirable for internet facing websites.

Related

Asp.Net MVC ActionResult with File

In Asp.Net MVC, we need to show a Html page, but also when that page shows, download a file too as the result of a form post.
Is there a kind of ActionResult that both renders HTML, but also cause the browser to download a file? Think of a page that shows "Here's your requested file" and the file starts to download.
Basically a combination of ActionResult and FileResult in one.
Here's a controller example that returns a file. I added an Iframe to the view that targets the controller method. I set the hidden attribute so the iframe doesn't show anywhere in the page. Hope you can use the solution. It seems to work very smoothly.
[HttpGet]
public FileResult GetPDF()
{
string fileName = "test.pdf";
string filePath = HttpContext.Server.MapPath(string.Format("~/Content/{0}", fileName));
byte[] fileBytes = System.IO.File.ReadAllBytes(filePath);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
Add the following code to your view.
<iframe hidden="hidden" src="#Url.Content("~/Home/Home/GetPDF")"></iframe>

How to show a pdf file in the browser tab from an action method

I'm trying to do pdf viewer functionality in mvc application. When user click the "read the pdf" link it should open a new tab/window and user should be able view the pdf file. So I checked the examples but I couldn't find. Could you suggest me any article or example ?
Show an anchor tag in your first view and pass an id (to identify what PDF to show)
#Html.ActionLink("read the pdf","view","doc",new { #id=123},null)
Now in the doc controller, have an action method which have a parameter called id and return the pdf there using the File method.
public ActionResult View(int id)
{
byte[] byteArrayOfFile=GetFieInByteArrayFormatFromId(id);
return File(byteArrayOfFile,"application/pdf");
}
Assuming GetFileInByteArrayFormatFromId is the method which returns the byte array format of the PDF file.
You can also return the PDF if you know the full path to the PDF file physically stored, using this overload.
public ActionResult Show()
{
string path="FullPAthTosomePDFfile.pdf";
return File(path, "application/pdf","someFriendlyName.pdf");
}
Show the PDF in a browser without downloading it
Based on the browser setting of the end user, the above solution will either ask user whether he/she wishes to download or open the file or simply download/open the file. If you prefer to show the file content directly in the browser without it gets downloaded to the user's computer, you may send a filestream to the browser.
public ActionResult Show(int id)
{
// to do : Using the id passed in,build the path to your pdf file
var pathToTheFile=Server.MapPath("~/Content/Downloads/sampleFile.pdf");
var fileStream = new FileStream(pathToTheFile,
FileMode.Open,
FileAccess.Read
);
return new FileStreamResult(fileStream, "application/pdf");
}
The above code expects you to have a pdf file named sampleFile.pdf in ~/Content/Downloads/ location. If you store the file(s) with a different name/naming convention, you may update the code to build the unique file name/path from the Id passed in.
If you want to display the PDF Content in browser, you can use iTextShare dll.
Refer the link http://www.codeproject.com/Tips/387327/Convert-PDF-file-content-into-string-using-Csharp.
Reading PDF content with itextsharp dll in VB.NET or C#

ASP.NET MVC: returning a CDN images from controller

I know how to return an image from the MVC controller but was wondering if returning an image in the CDN from the MVC controller will defeat the purpose of using CDN for images. Will using the following code result in the web server hosting the web application downloading from the CDN and then making it available to the user for download from the web server? Is the image downloaded twice, once from CDN to webserver and then webserver to user? If so that's worse than hosting the images directly on the webserver? Or is the image downloaded only once, directly from CDN to the end user?
How do I get to return an image from an MVC controller and at the same time take advantages of the CDN?
public ActionResult Thumb(int id)
{
//process
var file = Server.MapPath(" path to image in the CDN ");
//do your stuff
return File(file, "image/jpg", Path.GetFileName(file));
}
In your controller action you need to perform an HTTP request to fetch the image from the remote server:
public ActionResult Thumb(int id)
{
using (var client = new WebClient())
{
byte[] image = client.DownloadData("http://cdn.foo.com/myimage.jpg");
return File(image, "image/jpg");
}
}
and then:
<img src="#Url.Action("Thumb")" alt="" />
Obviously now the image is downloaded twice. Once in the controller from the CDN and once from the client. This completely defeats the purpose of this controller action and you could directly reference the image from the CDN:
<img src="http://cdn.foo.com/myimage.jpg" alt="" />
This obviously assumes that the client has access to the CDN.
Of course if your controller action does more than just fetching an image from a CDN and streaming it to the client like for example fetching the image from a CDN and resizing it, then you should definitely take the first approach.
Server.MapPath is used to map the physical path of an image, it's not going to do anything for a CDN image, because it doesn't exist locally at a physical path. And no, you don't want to return a file.
Your HTML would simply reference the CDN image in an <img> tag. ie
<img src="http://mycdn.com/image" />
Assuming you need to do some computing to figure out the resource first, like do a lookup based on some ID, to resolve the CDN url. You should absolutely not use a web client and download the image to a byte array. If you have to download the file, it essentially becomes a volatile resource and you need to decide how to deal with it, using a circuit breaker, probably with no cache, and you need to buffer it through so that it doesn't use too much memory.
In this case, you're best resolving the url and using a redirect 301/302 depending on the use case.
public ActionResult Thumb(int id)
{
//resolve url
var file = " path to image in the CDN ";
//redirect permanent
return RedirectPermanent(file);
//OR redirect
return Redirect(file);
}
Don't return the the actual image from the controller. That is worse because then you download it twice (CDN -> server -> client). You don't need a Thumb action at all.
If you need to generate the link to the file on the CDN in the controller, then just add a property to the view's model for the CDN link(s) and set it in the controller.
Create the link in your controller:
public ActionController SomeAction(int id){
var model = new SomeActionViewModel();
model.CDNLink = // do some stuff to generate CDN Link and set them on the model;
return View(model);
}
Then finally set it in your view
<img src="#Model.CDNLink" alt=""/>
I use URL Rewrite module 2.0 outboundRules.
Change response html Img(images),Link(css),Script(js) tags url.
URL Rewrite Module 2.0 Configuration Reference : URL Rewrite Module 2 : URL Rewrite Module : The Official Microsoft IIS Site
Current response
...
<img src="/images/photo1.jpg" />
<img src="/images/photo2.jpg" />
...
Set web.config urlrewrite configuration.
<rewrite>
<outboundRules>
<rule name="cdn" preCondition="html" enabled="true">
<match filterByTags="Img, Link, Script"
pattern="^/(images|csc|js)/(.*)" />
<action type="Rewrite" value="//cdn.local/{R:1}/{R:2}" />
</rule>
<preConditions>
<preCondition name="html">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="text/html" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
Rewrite response
...
<img src="//cdn.local/images/photo1.jpg" />
<img src="//cdn.local/images/photo2.jpg" />
...
Hope this help.
CDN configurations can vary, but the most common setup is that the CDN server will act as a large distributed proxy for your server (the "origin server").
The CDN doesn't care how you deliver your content. You can tell it to proxy for content by URL, by content type, or other factors.
So your situation, assuming the content, once delivered from a controller, won't change, can be set up as follows:
Request to CDN server
CDN server requests to your controller method on the origin server
Controller method generates content
CDN caches content for subsequent requests
CDN returns content
Subsequent requests for the same content by URL are returned from CDN
The most important factor in general when returning static content is the URL structure you use.
CDN does (can) care about the response headers in terms of caching and expires, but that is best avoided if you can finesse the URL correctly to provide unique content by URL (to solve versioning issues, for example).
If you are trying to secure the content, that is also possible with CDN but generally requires CDN-specific techniques.

CSS file only loads in IE when fetched via an ASP.NET MVC Controler + Action

I'm serving up a site's CSS content via an ASP.NET MVC's Controller + Action. The css "file" appears to deliver correctly down the wire but only IE will apply it. Other browsers (Firefox, Opera, Chrome) ignore the CSS file and render the page without styling. IE8 works perfectly.
This is the essential code that I'm using to return the CSS via the controller and action:
public void CSS(string version)
{
string cssFile = Server.MapPath("/site.css");
string cssContents = System.IO.File.ReadAllText(cssFile);
Response.Write(cssContents);
}
Note that the version can be any string. I have tried with strings such as "myversion.css", "1.css", "1234", "arbitrary" etc.
The all work in IE8 but not in any other browser. Any ideas?
You'll have to send the proper Content-type: text/css header.
Firefox will ignore the style sheet otherwise, and output a warning in its error console. Same goes probably for the other Gecko/Webkit based browsers.
This was one of those cases where after typing out the question and pondering how to phrase it and then posting it the answer dawned on me. When I returned here Pekka had correctly answered the question. If anybody's interested here's some code that fixes the problem:
string fileLocation = Request.PhysicalApplicationPath + #"site.css";
ContentResult fcr = new ContentResult();
fcr.Content = System.IO.File.ReadAllText(fileLocation);
fcr.ContentType = "text/css";
return fcr;

Serving a view as a file in asp.net mvc

I'm trying to send a view that contains a html table as a downloadable file to the user, as an excel file.
I keep getting the error "Server cannot set content type after HTTP headers have been sent.". I can't figure out what's going wrong...
Here's some code:
Excel.aspx:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<html>
<head runat="server">
<title>Excel</title>
</head>
<body>
....
</body>
</html>
ControllerAction:
public FileResult Excel()
{
string view = RenderViewToString(this.ControllerContext, "~/Views/Shared/Excel.aspx", null, this.ViewData, this.TempData);
MemoryStream stream = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(view));
string mimetype = RainbowsDotNet.FileHandling.MimeType.GetMimetypeFromExtension(".xls");
FileStreamResult filestreamresult = new FileStreamResult(stream, mimetype);
filestreamresult.FileDownloadName = "Employees_{0}.xls".FormatWith(DateTime.Now.ToString("ddMMyyyy_HHmmss"));
return filestreamresult;
}
While debugging, string "view" contains:
"\r\n<html>\r\n<body>............................"
Any idea?
I do about the exact same thing with a blob and that nicely returns a document to download.
You could get around this issue by putting this in the controller. It allows you to put HTML in a view, and then send it to the browser.
public ActionResult Excel()
{
this.Response.AddHeader("Content-Disposition", "Employees_{0}.xls".FormatWith(DateTime.Now.ToString("ddMMyyyy_HHmmss")));
this.Response.ContentType = "application/vnd.ms-excel";
//Do model stuff
Model model = new Model();
return View(model);
}
Sounds hacky? It is a little. I had the same problem you mentioned, and the question Jeff points to is also one of mine. :)
As mentioned in my comment, you should ensure that your view does not have the following:
<html>
<head>
...
</head>
<body>
</body>
</html>
None of this is needed, and may result in your page being rendered as HTML, rather than the Excel document being returned. So all you'll have rendered is the actual table tags and everything inside.
Did you use the RenderViewToString method from this post: Render a view as a string?
If you did then there is a Response.Flush in that code which is sending the headers. Buffering is enabled by default but if you call Response.Flush then everything is sent down to the client. And then why you try to send the file with the updated headers, you get that error.

Resources