I am calling a Web API from MVC project. The Web API is returning PDF file that I want to show directly in the browser after clicking on a button. My problem is when I click on the link, it downloads the pdf file and shows the icon at the left bottom corner side and I have to click on it and to open the PDf in acrobat. How I can make it the way that by clicking the link it open the pdf directly in the browser?
This is my code in MVC project that open the pdf:
[HttpGet]
public FileResult openPdf(string name)
{
byte[] pdfByte = DownloadFile();
return File(pdfByte, "application/pdf", name);
}
internal byte[] DownloadFile()
{
string serverUrl = "http://localhost/GetPdf?Number=3671";
var client = new System.Net.WebClient();
client.Headers.Add("Content-Type", "application/pdf");
return client.DownloadData(serverUrl);
}
This is the method in my Web API that returns pdf:
public HttpResponseMessage GetPdfNameByRemRef(string RemoteRefNumber)
{
var stream = new MemoryStream();
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(stream.GetBuffer())
};
byte[] fileBytes = System.IO.File.ReadAllBytes(#"C:\Pdf\CreditApplication_08192006_102714AM_et montis.pdf");
response.Content = new ByteArrayContent(fileBytes);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = customerInfo.Application_Filename;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
}
You can try to add Content-Disposition header with value inline.
Response.AddHeader("Content-Disposition", "inline;filename=fileName.pdf");
However, the behavior can differ on different browsers and on file types you are serving. If Content-Disposition is set to inline the browser will try to open the file within the browser, but it may fail if the file type is unknown (ex. .rar, .zip, .pdf /when pdf reader plugin is missing/, if the browser is old .. etc.).
Related
I want to let user to download a file from server.
I looked up for the solution and when trying to make an example - ended up with this:
#Route("test-download")
public class Download extends VerticalLayout {
public Download() {
Anchor downloadLink = new Anchor(createResource(), "Download");
downloadLink.getElement().setAttribute("download", true);
add(downloadLink);
}
private AbstractStreamResource createResource() {
return new StreamResource("/home/johny/my/important-file.log", this::createExportr);
}
private InputStream createExportr(){
return null;
}
}
Which is giving java.lang.IllegalArgumentException: Resource file name parameter contains '/' when I go to the page in browser.
How do I make a download button (or anchor) knowing file location on disk?
Have a look at the documentation, paragraph "Using StreamResource". The first parameter is just a file name that will be used by the browser to propose that file name to the user when downloading. So you could pass it like "important-file.log". The content of the download is provided by the InputStream parameter. For instance, you could read from your file, see here:
File initialFile = new File("src/main/resources/sample.txt");
InputStream targetStream = new FileInputStream(initialFile);
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!
The following returns a PDF which the browser tries to directly display inline. This works correctly. However, if I try to download the file, the download name is not "myPDF.pdf", but instead the ID in the route (myapp/controller/PDFGenerator/ID). Is it possible to set the file download name to be "myPDF.pdf"?
public FileStreamResult PDFGenerator(int id)
{
MemoryStream ms = GeneratePDF(id);
byte[] file = ms.ToArray();
MemoryStream output = new MemoryStream();
output.Write(file, 0, file.Length);
output.Position = 0;
HttpContext.Response.AddHeader("content-disposition",
"inline; filename=myPDF.pdf");
return File(output, "application/pdf", fileDownloadName="myPDF.pdf");
}
No, this is not possible with a PDF displayed inline. You could achieve this if you send the Content-Disposition header with as an attachment:
public ActionResult PDFGenerator(int id)
{
Stream stream = GeneratePDF(id);
return File(stream, "application/pdf", "myPDF.pdf");
}
Also notice how I removed the unnecessary MemoryStream you were using and loading the PDF in memory where you could have directly streamed it to the client which would have been far more efficient.
If you are using FileStreamResult to download the file, try using this in controller
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment; filename=FileName.pdf");
It is possible by making the id a string which represents the file name without the extension.
public ActionResult PDFGenerator(string id, int? docid)
{
Stream stream = GeneratePDF(docid);
return new FileStreamResult(stream , "application/pdf");
}
The url then then end like this
..PDFGenerator/Document2?docid=15
This is My Action:
public FileResult ShowFile(long Id) {
DAL.Files.File file = new DAL.Files.FileAccess().GetById(Id);
return File(file.Content, file.FileType);
}
GetById method return a file from SQL DB, when the file type is text or picture every thing is OK and correctly shown file, but when the file type is different like PDF that show some Unicode, so how can I shown PDF file correctly, or let the user download it?
The second argument to the File() method needs to be a mime type. So say your file is a PDF, it should work if you do this:
public FileResult ShowFile(long Id) {
DAL.Files.File file = new DAL.Files.FileAccess().GetById(Id);
// assuming file is a PDF
return File(file.Content, "application/pdf");
}
To force a download instead, use the File() overload that takes a 3rd argument:
public FileResult ShowFile(long Id) {
DAL.Files.File file = new DAL.Files.FileAccess().GetById(Id);
// assuming file is a PDF
return File(file.Content, "application/pdf", "some-pdf-file.pdf");
}
As mentioned above you need to add the appklication/pdf, however I also needed the headers to be correct.
public FileResult ShowFile(long Id)
{
DAL.Files.File file = new DAL.Files.FileAccess().GetById(Id);
// assuming file is a PDF
Response.AppendHeader("Content-Disposition", "inline; filename=CustomInvoice.pdf");
return File(file.Content, "application/pdf", "some-pdf-file.pdf");
}
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