I'm using MvcRazorToPdf in a Azure website and create my PDF's and output them in the browser.
Now i'm creating a new function to directly email the PDF as attachment (without output them in the browser).
Does anybody know if it is possible to save the PDF (with MvcRazorToPdf) as a MemoryStream or Byte[]?
I think you can handle this in ResultFilter, I used below code to allow user to download file and prompt for download popup, in this way you can grab all your memory stream and store somewhere to send email afterwords.
public class ActionDownloadAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.AddHeader("content-disposition", "attachment; filename=" + "Report.pdf");
base.OnResultExecuted(filterContext);
}
}
[ActionDownload]
public ActionResult GeneratePdf()
{
List<Comment> comments = null;
using (var db = new CandidateEntities())
{
comments = db.Comments.ToList();
}
return new PdfActionResult("GeneratePdf", comments);
}
I have implemented something like that. So basically I have not been changing my method to output PDF. What I have done is used restsharp to make request at URL where I get PDF then what you have is in lines of (this is partial code only so you can get idea )
var client = new RestClient(IAPIurl);
var request = new RestRequest(String.Format(IAPIurl_generatePDF, targetID), Method.GET);
RestResponse response = (RestResponse) client.Execute(request);
// Here is your byte array
response.RawBytes
Otherwise you can use my answer from here where I discussed directly returning a file.
Hope this helps!
Related
In my controller, I am rendering a view.
My Action method looks like this:
public ActionResult SomePrint(Model model)
{
//Some business action
return View("viewname",model);
}
Now my requirement is to save this view as file(may be pdf file) in my solution and send it to print and delete the file once the print is done.
Tried to use Rotativa and convert it to pdf by following
public ActionResult DownloadViewPDF()
{
var model = new GeneratePDFModel();
//Code to get content
return new Rotativa.ViewAsPdf("GeneratePDF", model){FileName = "TestViewAsPdf.pdf"}
}
But i need it to save it as pdf and print the same.
Any help? Thanks in advance.
If you would have been requesting to export to a known convertible type (such as Excel), formatting the stream would be enough. But if you would like to Export to PDF you should create another View to Export the file and use a 3rd party application such as iText.
You can use BuildPdf method on ViewAsPdf.
public ActionResult DownloadViewPDF()
{
var model = new GeneratePDFModel();
var pdfResult = new ViewAsPdf("GeneratePDF", model)
{ FileName = "TestViewAsPdf.pdf" };
var binary = pdfResult.BuildPdf(this.ControllerContext);
// you can save the binary pdf now
return File(binary, "application/pdf");
}
I want to know is there any open source action result code available for asp.net mvc in which i can pass a URL and it will response out the content.
Just like there are some inbuilt Action results
FileResult
FileStreamResult
I want to build a custom Action Result in which i can pass a URL (basically an mp3 url) which will get downloaded in memory and the content will be streamed to the current executing response.
It should also support resume download if supported by server and client. Thats a must for me.
public UrlActionResult DownloadUrl(string url)
{
return new UrlActionResult("http://www.example.com/audiofile.mp3");
}
For a basic scenario you could use something like this:
public class DownloadResult : ActionResult
{
private readonly FileStreamResult _fileStreamResult;
public DownloadResult(string url, string contentType)
{
using (var myWebClient = new WebClient())
{
var myStream = myWebClient.OpenRead(url);
_fileStreamResult = new FileStreamResult(myStream, contentType);
}
}
public override void ExecuteResult(ControllerContext context)
{
_fileStreamResult.ExecuteResult(context);
}
}
However if you want to do the resume download it becomes a lot more complicated. Here is a great article with example code.
If you don't need to hide the url just use a redirect. Using a redirect with spare you server bandwidth:
public ActionResult DownloadUrl(string url)
{
return new Redirect("http://www.example.com/audiofile.mp3");
}
I implemented a POST Rest service to upload files to my server. the problem i have right now is that i want to restrict the uploaded files by its type. lets say for example i only want to allow .pdf files to be uploaded.
What I tried to do was
Task<Stream> task = this.Request.Content.ReadAsStreamAsync();
task.Wait();
FileStream requestStream = (FileStream)task.Result;
but unfortunately its not possible to cast the Stream to a FileStream and access the type via requestStream.Name.
is there an easy way (except writing the stream to the disk and check then the type) to get the filetype?
If you upload file to Web API and you want to get access to file data (Content-Disposition) you should upload the file as MIME multipart (multipart/form-data).
Here I showed some examples on how to upload from HTML form, Javascript and from .NET.
You can then do something like this, this example checks for pdf/doc files only:
public async Task<HttpResponseMessage> Post()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable,
"This request is not properly formatted - not multipart."));
}
var provider = new RestrictiveMultipartMemoryStreamProvider();
//READ CONTENTS OF REQUEST TO MEMORY WITHOUT FLUSHING TO DISK
await Request.Content.ReadAsMultipartAsync(provider);
foreach (HttpContent ctnt in provider.Contents)
{
//now read individual part into STREAM
var stream = await ctnt.ReadAsStreamAsync();
if (stream.Length != 0)
{
using (var ms = new MemoryStream())
{
//do something with the file memorystream
}
}
}
return Request.CreateResponse(HttpStatusCode.OK);
}
}
public class RestrictiveMultipartMemoryStreamProvider : MultipartMemoryStreamProvider
{
public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
{
var extensions = new[] {"pdf", "doc"};
var filename = headers.ContentDisposition.FileName.Replace("\"", string.Empty);
if (filename.IndexOf('.') < 0)
return Stream.Null;
var extension = filename.Split('.').Last();
return extensions.Any(i => i.Equals(extension, StringComparison.InvariantCultureIgnoreCase))
? base.GetStream(parent, headers)
: Stream.Null;
}
}
Is there a way to stream a file using ASP.NET MVC FileContentResult within the browser with a specific name?
I have noticed that you can either have a FileDialog (Open/Save) or you can stream the file in a browser window, but then it will use the ActionName when you try to save the file.
I have the following scenario:
byte[] contents = DocumentServiceInstance.CreateDocument(orderId, EPrintTypes.Quote);
result = File(contents, "application/pdf", String.Format("Quote{0}.pdf", orderId));
When I use this, I can stream the bytes, but a OPEN/SAVE file dialog is given to the user. I would like to actually stream this file in a browser window.
If I just use the FilePathResult, it shows the file in a browser window, but then when I click on "Save" button to save the file in PDF, it shows me the Action Name as the name of the file.
Has anyone encountered this?
public ActionResult Index()
{
byte[] contents = FetchPdfBytes();
return File(contents, "application/pdf", "test.pdf");
}
and for opening the PDF inside the browser you will need to set the Content-Disposition header:
public ActionResult Index()
{
byte[] contents = FetchPdfBytes();
Response.AddHeader("Content-Disposition", "inline; filename=test.pdf");
return File(contents, "application/pdf");
}
Actually, the absolutely easiest way is to do the following...
byte[] content = your_byte[];
FileContentResult result = new FileContentResult(content, "application/octet-stream")
{
FileDownloadName = "your_file_name"
};
return result;
This might be helpful for whoever else faces this problem. I finally figured out a solution. Turns out, even if we use the inline for "content-disposition" and specify a file name, the browsers still do not use the file name. Instead browsers try and interpret the file name based on the Path/URL.
You can read further on this URL:
Securly download file inside browser with correct filename
This gave me an idea, I just created my URL route that would convert the URL and end it with the name of the file I wanted to give the file. So for e.g. my original controller call just consisted of passing the Order Id of the Order being printed. I was expecting the file name to be of the format Order{0}.pdf where {0} is the Order Id. Similarly for quotes, I wanted Quote{0}.pdf.
In my controller, I just went ahead and added an additional parameter to accept the file name. I passed the filename as a parameter in the URL.Action method.
I then created a new route that would map that URL to the format:
http://localhost/ShoppingCart/PrintQuote/1054/Quote1054.pdf
routes.MapRoute("", "{controller}/{action}/{orderId}/{fileName}",
new { controller = "ShoppingCart", action = "PrintQuote" }
, new string[] { "x.x.x.Controllers" }
);
This pretty much solved my issue.
Previous answers are correct: adding the line...
Response.AddHeader("Content-Disposition", "inline; filename=[filename]");
...will causing multiple Content-Disposition headers to be sent down to the browser. This happens b/c FileContentResult internally applies the header if you supply it with a file name. An alternative, and pretty simple, solution is to simply create a subclass of FileContentResult and override its ExecuteResult() method. Here's an example that instantiates an instance of the System.Net.Mime.ContentDisposition class (the same object used in the internal FileContentResult implementation) and passes it into the new class:
public class FileContentResultWithContentDisposition : FileContentResult
{
private const string ContentDispositionHeaderName = "Content-Disposition";
public FileContentResultWithContentDisposition(byte[] fileContents, string contentType, ContentDisposition contentDisposition)
: base(fileContents, contentType)
{
// check for null or invalid ctor arguments
ContentDisposition = contentDisposition;
}
public ContentDisposition ContentDisposition { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
// check for null or invalid method argument
ContentDisposition.FileName = ContentDisposition.FileName ?? FileDownloadName;
var response = context.HttpContext.Response;
response.ContentType = ContentType;
response.AddHeader(ContentDispositionHeaderName, ContentDisposition.ToString());
WriteFile(response);
}
}
In your Controller, or in a base Controller, you can write a simple helper to instantiate a FileContentResultWithContentDisposition and then call it from your action method, like so:
protected virtual FileContentResult File(byte[] fileContents, string contentType, ContentDisposition contentDisposition)
{
var result = new FileContentResultWithContentDisposition(fileContents, contentType, contentDisposition);
return result;
}
public ActionResult Report()
{
// get a reference to your document or file
// in this example the report exposes properties for
// the byte[] data and content-type of the document
var report = ...
return File(report.Data, report.ContentType, new ContentDisposition {
Inline = true,
FileName = report.FileName
});
}
Now the file will be sent to the browser with the file name you choose and with a content-disposition header of "inline; filename=[filename]".
I hope that helps!
The absolute easiest way to stream a file into browser using ASP.NET MVC is this:
public ActionResult DownloadFile() {
return File(#"c:\path\to\somefile.pdf", "application/pdf", "Your Filename.pdf");
}
This is easier than the method suggested by #azarc3 since you don't even need to read the bytes.
Credit goes to: http://prideparrot.com/blog/archive/2012/8/uploading_and_returning_files#how_to_return_a_file_as_response
** Edit **
Apparently my 'answer' is the same as the OP's question. But I am not facing the problem he is having. Probably this was an issue with older version of ASP.NET MVC?
I adapted it in ASP.NET Core with REST API.
public class FileContentWithFileNameResult : FileContentResult
{
public FileContentWithFileNameResult(byte[] fileContents, string contentType, string fileName)
: base(fileContents, contentType)
{
FileName = fileName;
}
public string FileName { get; private set; }
public override Task ExecuteResultAsync(ActionContext context)
{
var response = context.HttpContext.Response;
response.Headers.Append("Content-Disposition", $"inline; filename={FileName}");
response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
response.Headers.Append("X-Content-Type-Options", "nosniff");
return base.ExecuteResultAsync(context);
}
}
public FileContentResult GetImage(int productId) {
Product prod = repository.Products.FirstOrDefault(p => p.ProductID == productId);
if (prod != null) {
return File(prod.ImageData, prod.ImageMimeType);
} else {
return null;
}
}
In WebForms, I would normally have code like this to let the browser present a "Download File" popup with an arbitrary file type, like a PDF, and a filename:
Response.Clear()
Response.ClearHeaders()
''# Send the file to the output stream
Response.Buffer = True
Response.AddHeader("Content-Length", pdfData.Length.ToString())
Response.AddHeader("Content-Disposition", "attachment; filename= " & Server.HtmlEncode(filename))
''# Set the output stream to the correct content type (PDF).
Response.ContentType = "application/pdf"
''# Output the file
Response.BinaryWrite(pdfData)
''# Flushing the Response to display the serialized data
''# to the client browser.
Response.Flush()
Response.End()
How do I accomplish the same task in ASP.NET MVC?
Return a FileResult or FileStreamResult from your action, depending on whether the file exists or you create it on the fly.
public ActionResult GetPdf(string filename)
{
return File(filename, "application/pdf", Server.UrlEncode(filename));
}
To force the download of a PDF file, instead of being handled by the browser's PDF plugin:
public ActionResult DownloadPDF()
{
return File("~/Content/MyFile.pdf", "application/pdf", "MyRenamedFile.pdf");
}
If you want to let the browser handle by its default behavior (plugin or download), just send two parameters.
public ActionResult DownloadPDF()
{
return File("~/Content/MyFile.pdf", "application/pdf");
}
You'll need to use the third parameter to specify a name for the file on the browser dialog.
UPDATE: Charlino is right, when passing the third parameter (download filename) Content-Disposition: attachment; gets added to the Http Response Header. My solution was to send application\force-download as the mime-type, but this generates a problem with the filename of the download so the third parameter is required to send a good filename, therefore eliminating the need to force a download.
You can do the same in Razor or in the Controller, like so..
#{
//do this on the top most of your View, immediately after `using` statement
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment; filename=receipt.pdf");
}
Or in the Controller..
public ActionResult Receipt() {
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment; filename=receipt.pdf");
return View();
}
I tried this in Chrome and IE9, both is downloading the pdf file.
I probably should add I am using RazorPDF to generate my PDFs. Here is a blog about it: http://nyveldt.com/blog/post/Introducing-RazorPDF
You should look at the File method of the Controller. This is exactly what it's for. It returns a FilePathResult instead of an ActionResult.
mgnoonan,
You can do this to return a FileStream:
/// <summary>
/// Creates a new Excel spreadsheet based on a template using the NPOI library.
/// The template is changed in memory and a copy of it is sent to
/// the user computer through a file stream.
/// </summary>
/// <returns>Excel report</returns>
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult NPOICreate()
{
try
{
// Opening the Excel template...
FileStream fs =
new FileStream(Server.MapPath(#"\Content\NPOITemplate.xls"), FileMode.Open, FileAccess.Read);
// Getting the complete workbook...
HSSFWorkbook templateWorkbook = new HSSFWorkbook(fs, true);
// Getting the worksheet by its name...
HSSFSheet sheet = templateWorkbook.GetSheet("Sheet1");
// Getting the row... 0 is the first row.
HSSFRow dataRow = sheet.GetRow(4);
// Setting the value 77 at row 5 column 1
dataRow.GetCell(0).SetCellValue(77);
// Forcing formula recalculation...
sheet.ForceFormulaRecalculation = true;
MemoryStream ms = new MemoryStream();
// Writing the workbook content to the FileStream...
templateWorkbook.Write(ms);
TempData["Message"] = "Excel report created successfully!";
// Sending the server processed data back to the user computer...
return File(ms.ToArray(), "application/vnd.ms-excel", "NPOINewFile.xls");
}
catch(Exception ex)
{
TempData["Message"] = "Oops! Something went wrong.";
return RedirectToAction("NPOI");
}
}
Although standard action results FileContentResult or FileStreamResult may be used for downloading files, for reusability, creating a custom action result might be the best solution.
As an example let's create a custom action result for exporting data to Excel files on the fly for download.
ExcelResult class inherits abstract ActionResult class and overrides the ExecuteResult method.
We are using FastMember package for creating DataTable from IEnumerable object and ClosedXML package for creating Excel file from the DataTable.
public class ExcelResult<T> : ActionResult
{
private DataTable dataTable;
private string fileName;
public ExcelResult(IEnumerable<T> data, string filename, string[] columns)
{
this.dataTable = new DataTable();
using (var reader = ObjectReader.Create(data, columns))
{
dataTable.Load(reader);
}
this.fileName = filename;
}
public override void ExecuteResult(ControllerContext context)
{
if (context != null)
{
var response = context.HttpContext.Response;
response.Clear();
response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
response.AddHeader("content-disposition", string.Format(#"attachment;filename=""{0}""", fileName));
using (XLWorkbook wb = new XLWorkbook())
{
wb.Worksheets.Add(dataTable, "Sheet1");
using (MemoryStream stream = new MemoryStream())
{
wb.SaveAs(stream);
response.BinaryWrite(stream.ToArray());
}
}
}
}
}
In the Controller use the custom ExcelResult action result as follows
[HttpGet]
public async Task<ExcelResult<MyViewModel>> ExportToExcel()
{
var model = new Models.MyDataModel();
var items = await model.GetItems();
string[] columns = new string[] { "Column1", "Column2", "Column3" };
string filename = "mydata.xlsx";
return new ExcelResult<MyViewModel>(items, filename, columns);
}
Since we are downloading the file using HttpGet, create an empty View without model and empty layout.
Blog post about custom action result for downloading files that are created on the fly:
https://acanozturk.blogspot.com/2019/03/custom-actionresult-for-files-in-aspnet.html
Use .ashx file type and use the same code