How do I write FileContentResult on disk? - asp.net-mvc

I am trying to use the Rotativa component to store (not to show) a copy of the invoice permanently on web server disk. Two questions:
Why I need to specify a controller action? ("Index", in this
case)
How do I write the FileContentResult on local disk without
displaying it?
Thanks.
Here is my code:
[HttpPost]
public ActionResult ValidationDone(FormCollection formCollection, int orderId, bool fromOrderDetails)
{
Order orderValidated = context.Orders.Single(no => no.orderID == orderId);
CommonUtils.SendInvoiceMail(orderValidated.customerID , orderValidated.orderID);
var filePath = Path.Combine(Server.MapPath("/Temp"), orderValidated.invoiceID + ".pdf");
var pdfResult = new ActionAsPdf("Index", new { name = orderValidated.invoiceID }) { FileName = filePath };
var binary = pdfResult.BuildPdf(ControllerContext);
FileContentResult fcr = File(binary, "application/pdf");
// how do I save 'fcr' on disk?
}

You do not need the FileContentResult to create a file. You've got the byte array which can be saved directly to the disk:
var binary = pdfResult.BuildPdf(ControllerContext);
System.IO.File.WriteAllBytes(#"c:\foobar.pdf", binary);

string FileName="YOUR FILE NAME";
//first give a name to file
string Path=Server.MapPath("YourPath in solution"+Filename+".Pdf")
//Give your path and file extention. both are required.
binary[]= YOUR DATA
//Describe your data to be save as file.
System.IO.File.WriteAllBytes(Path, binary);
Thats simple...

Related

How to retrieve uploaded file in mvc 4

This is my controller code for uploading file , it is only storing the title in the database and actual file is stored in the documents/file folder now. I am facing problem in retrieving this uploaded file since I have stored only the title in database. How do I retrieve a file ?
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UploadFile(string Title)
{
_Db.Uploads.Add(new Upload() { Title = Title });
_Db.SaveChanges();
int Id = (from a in _Db.Uploads select a.Upload_id).Max();
if(Id>0)
{
if(Request.Files["file"].ContentLength>0)
{
string extension = System.IO.Path.GetExtension(Request.Files["file"].FileName);
string path1 = String.Format("{0}/{1}{2}", Server.MapPath("~/documents/Files"), Id, extension);
if (System.IO.File.Exists(path1))
System.IO.File.Delete(path1);
Request.Files["file"].SaveAs(path1);
}
ViewData["Sucess"] = "success";
}
else
{
ViewData["Success"] = "Upload Failed";
}
return View();
}
since I have stored only the title in database
_Db.Uploads.Add(new Upload() { Title = Title });
from a in _Db.Uploads select a.Upload_id
it appears you're also storing the ID, not just the title
The save path uses this ID.
string path1 = String.Format("{0}/{1}", Server.MapPath("~/documents/Files"), Id);
(I've removed the extension for now)
So simply read the file back using the ID of the file you want to reload:
[HttpGet]
public ActionResult DownloadFile(int ID)
{
string path1 = String.Format("{0}/{1}", Server.MapPath("~/documents/Files"), ID);
return FilePathResult(path);
}
(otomh, untested)
Of course, the best solution is to persist the filename+extension (no need for path if it's always the same) with the title and ID.
Edit: More details:
You currently store the Title and ID, change this to also store the filename path path1. Use this to retrieve the uploaded file.

How do I store an uploaded file to a location other than the application folder in MVC application

I can successfully upload a file using this code:
[HttpPost]
public ActionResult Save(IEnumerable<HttpPostedFileBase> attachments)
{
// The Name of the Upload component is "attachments"
foreach (var file in attachments)
{
// Some browsers send file names with full path. This needs to be stripped.
var fileName = Path.GetFileName(file.FileName);
var physicalPath = Path.Combine(Server.MapPath("~/App_Data"), fileName);
file.SaveAs(physicalPath);
}
// Return an empty string to signify success
return Content("");
}
But this puts the file in the running application App_Data folder. I want to put the file in a share on my LAN. I have tried the code below but it always appends myPath to the path of the application:
[HttpPost]
public ActionResult Save(IEnumerable<HttpPostedFileBase> attachments)
{
// The Name of the Upload component is "attachments"
foreach (var file in attachments)
{
// Some browsers send file names with full path. This needs to be stripped.
var fileName = Path.GetFileName(file.FileName);
//add possible new folder name to path
var myPath = "//my-server-myshare/myfolder/";
//combine with file name
var physicalPath = Path.Combine(Server.MapPath(myPath), fileName);
file.SaveAs(physicalPath);
}
// Return an empty string to signify success
return Content("");
}
How can I force this to save in the URL I have as myPath?
Network shares need to be full UNC paths, so flip your slashes around :
var myPath = #"\\my-server-myshare\myfolder\";
You could also just create the file directly :
file.Create(#"\\my-server-myshare\myfolder\");

Why do spaces in a file name cause IE to remove a file extension on file download?

I am using EPPlus to create an Excel file from a CSV which works without issue. Unfortunately, the below code causes Internet Explorer 9, 10, and 11 to drop the .xlsx file extension, while Chrome and Firefox do not. If I remove the spaces from the file name the file extension works as expected in IE.
public FileStreamResult DetailsExcel(string id)
{
string custName;
var csv = this.GetCsvForCustomer(id, out custName);
var fileName = String.Format("Report for {0} ({1:d-M-yyyy HH mm})",
custName, DateTime.Now);
MemoryStream stream;
using (var excelPackage = new ExcelPackage())
{
var ws = excelPackage.Workbook.Worksheets.Add(fileName);
ws.Cells["A1"].LoadFromText(csv, this._excelTextFormat);
stream = new MemoryStream(excelPackage.GetAsByteArray());
}
return File(stream,
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
fileName + ".xlsx");
}
The two programmatic mechanisms that I have found that somewhat work are to wrap the file name in quotes or to UrlEncode the string. Each have issues:
String.Format("\"{0}\".xlsx", fileName)
// above renders: __Report for Customer (20-2-2014 11 04)__.xlsx
HttpUtility.UrlEncode(fileName + ".xlsx")
// above renders: Report+for+Customer+(20-2-2014+11+04).xlsx
Neither of the above are optimal. How do I include spaces in a file name without losing the file extension when a user is browsing with IE?
I haven't been able to reproduce the behavior you describe in a similar application that I have. The only difference is that I'm not loading the byte array into a MemoryStream first, but just passing the byte array to File.
So try removing the MemoryStream from the equation. So the code would end up something like:
public ActionResult DetailsExcel(string id)
{
byte[] stream; // changed to byte array
using (var excelPackage = new ExcelPackage())
{
var ws = excelPackage.Workbook.Worksheets.Add(fileName);
ws.Cells["A1"].LoadFromText(csv, this._excelTextFormat);
stream = excelPackage.GetAsByteArray(); // removed MemoryStream
}
return File(stream,
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
fileName + ".xlsx");
}

Convert HttpPostBasicFile to Image in MVC

I get a picture by uploading and I want to convert it to image file without save it.
how can I do it?
public HttpPostedFileBase BasicPicture { get; set; }
var fileName = Path.GetFileName(BasicPicture.FileName);
// store the file inside ~/App_Data/uploads folder
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
BasicPicture.SaveAs(path);
By this code I can save the picture on the server but I want convert it to image
like
Image img=(Image) BasicPicture;
but it doesn't work.
You could use the FromStream method:
using (Image img = Image.FromStream(BasicPicture.InputStream))
{
... do something with the image here
}
You can also convert HttpPostedFileBase to WebImage (which gives you more API - like method Resize):
public ActionResult SaveUploadedImage(HttpPostedFileBase file)
{
if(file != null)
{
var image = new System.Web.Helpers.WebImage(file.InputStream);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), file.FileName);
image.Save(path);
}
return View();
}
With out knowing exactly what you are doing and why i can give a full intelligent answer.
Personally i would use something like this to open an image. You have saved the image to your server, so instead of casting why not new up a new image? the end result is the same!
WebImage webImage = new WebImage(path);

Stream file using ASP.NET MVC FileContentResult in a browser with a name?

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;
}
}

Resources