ASP.NET MVC FileStreamResult not working as intended - asp.net-mvc

I have the following code which I stripped out of any non-essential lines to leave the minimun reproducable case. What I expect is for it to return the image, but it doesn't. As far as I can see it returns an empty file:
public ActionResult Thumbnail(int id) {
var question = GetQuestion(db, id);
var image = new Bitmap(question.ImageFullPath);
MemoryStream stream = new MemoryStream();
image.Save(stream, ImageFormat.Jpeg);
return new FileStreamResult(stream, "image/jpeg");
}
Can you identify what's wrong with this code? In the debugger I can see that the stream grows in size so it seems to be getting the data although I haven't been able to verify it's the correct data. I have no idea how to debug the FileStreamResult itself.

You need to insert
stream.Seek(0, SeekOrigin.Begin);
after the call to
Image.Save()
This will rewind the stream to the beginning of the saved image. Otherwise the stream will be positioned at the end of the stream and nothing is sent to the receiver.

Try rewinding the MemoryStream. The "cursor" is left at the end of the file and there is nothing to read until you "rewind" the stream to the beginning.
image.Save( stream, ImageFormat.Jpeg );
stream.Seek( 0, SeekOrigin.Begin );
return new FileStreamResult( stream, "image/jpeg" );

Related

MVC NewtonSoft return large string

I am building a web application with MVC. For one of my views I need to fetch a large string and then bind it to jsTree. However, several times I am receiving the OutOfMemory exception. The string that is returned is with a length of 55,582,723 characters. I am dynamically creating a JArray with JObjects within the JArray.
Here is how I originally had it
var jArray = GetJArray();
return jArray.ToString();
With that code I received the exception very frequently. I then did some research and found out that I can write to a stream and then return the stream. So I changed the method to this:
var jArray = GetJArray();
var serializer = new JsonSerializer();
var ms = new MemoryStream();
var sw = new StreamWriter();
var writer = new JsonTextWriter(sw);
serializer.Serialize(writer, jArray);
writer.Flush();
ms.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(ms, "text/plain");
Now with this code, it has improved a lot, however, I am still in some cases getting an OutOfMemory exception. The string that is returned will always be the same length as mentioned above.
Any insight to this will be much appreciated.
EDIT: To add a little more information the exception happened in my original implementation at:
return jArray.ToString();
The exception occurs in the my second implementation at:
serializer.Serialize(writer, jArray);

MvcRazorToPdf save as MemoryStream or Byte[]

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!

ASP.NET MVC FileStreamResult, fileDownloadName is not used

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

iTextSharp creating file in memory resulting corrupted file

I am trying to create a pdf file in MVC using iTextSharp. I do have a following simple used case. File is getting created but when I open the PDF I am getting error file is corrupted unable to open the file. Any idea/help ?
My Controller code is a follows
public FileStreamResult GetPdfMemory()
{
iTextSharp.text.Document doc = new iTextSharp.text.Document();
MemoryStream mem = new MemoryStream();
PdfWriter pdfWriter = PdfWriter.GetInstance(doc, mem);
//pdfWriter.CloseStream = false;
doc.Open();
doc.Add(new Paragraph("Charts"));
mem.Position = 0;
FileStreamResult fileStreamResult = new FileStreamResult(mem, System.Net.Mime.MediaTypeNames.Application.Pdf)
{
FileDownloadName = "chart_" + ".PDF"
};
return fileStreamResult;
}
View :
#Html.ActionLink("Pdf Memory", "GetPdfMemory", "Home", null, new { id = "download"})
FYI : When I try to use FileStream instead of MemoryStream all works fine. But I need to create PDF using memorystream.
You manipulate (mem.Position = 0) and use (new FileStreamResult(mem, ...)) the MemoryStream before signalling iTextSharp that it can finalize the document. Thus, depending on whether implicit destruction of the Document and the PdfWriter or the use of the data in the memory stream comes first, you either have a PDF missing its closing parts or the closing parts (being written after you reposition the memory stream) overwriting the start of the data.
To signal to iTextSharp that it can finalize the document, please call doc.Close() before manipulating the memory stream or alternatively use the Document in a using block, e.g.:
using (MemoryStream ms = new MemoryStream()) {
// step 1
using (Document document = new Document()) {
// step 2
PdfWriter.GetInstance(document, ms);
// step 3
document.Open();
// step 4
document.Add(new Paragraph("HelloWorldMemory"));
}
HttpContext.Current.Response.BinaryWrite(ms.ToArray());
}
(shamelessly copied from the sample HelloWorldMemory.cs from the Webified iTextSharp Examples) for iText in Action — 2nd Edition)
Using using implicitly causes the Document to be closed.

Reading a HttpPostedFile before saving it saves a blank file in ASP.NET MVC

Before saving an uploaded csv file I want to check it will parse. When I was just saving the file everything was fine, now that I'm reading it first the file saved is blank.
Here is my action method
[HttpPost]
public ActionResult Import(HttpPostedFileBase file)
{
// Check parse went ok
using (var fileStream = file.InputStream)
{
if (!MemberFileParsingService.CheckFileWillParse(fileStream))
{
ViewBag.Message = "There was a problem with the file";
return View();
}
}
// Save file so we can work on it in next action
file.SaveAs(Server.MapPath(fileName));
return RedirectToAction("ImportMatch", new { club = ActiveClub.Url });
}
And here's my method that checks to see if the file parses ok. It uses CsvReader to read through the whole file to check there are no errors. CsvReader throws exceptions when it comes to bad bits of the file.
public static bool CheckFileWillParse(Stream fileStream)
{
try
{
using (var reader = new StreamReader(fileStream))
{
using (CsvReader csv = new CsvReader(reader, false))
{
while (csv.ReadNextRecord()) { }
}
}
}
catch(Exception)
{
return false;
}
return true;
}
I think it's probably because it's trying to write the file using the same stream that is now at the end of the file. I don't know how to reset the stream though. I was hoping all my using statements would fix that problem.
So how can I reset the stream, or is that just a red herring?
Update: Found the length of the stream gets reset to zero after going through CheckFileWillParse so looks like resetting the stream is just a red herring and the stream is actually being blanked somehow.
You have to rewind the stream (if possible). Once you are doing reading it, the current position is at the end of the stream, that is why the file is empty when you save it.
You can use either the Seek function or the Position property to do this (set it to 0). Not all stream types support this though.
If a stream type doesn't support it, you may need to write the file out to disk first, then run your test against it.
Have you considered creating a copy of the stream to analyse, using Stream.CopyTo()?
Because of the using statement, the Dispose() method on your StreamReader object will be called. This will actually close the underlying Stream object. Hence why the stream is of zero length.
Option 1:
One option is to not dispose of the StreamReader instance by removing the using statement. You will need to manually dispose of the stream later (but maybe CsvReader will do this for you) by calling its Dispose() method.
The garbage collector will clean up the StreamReader object and will not close the underlying stream.
Option 2:
You can use the following constructor when instantiating StreamReader:
public StreamWriter(
Stream stream,
Encoding encoding,
int bufferSize,
bool leaveOpen
)
Setting the leaveOpen parameter to true will ensure that the stream will not be closed.
Since the StreamReader Dispose Method will dispose your underlying Stream as well.
MemoryStream ms = new MemoryStream();
myStream.CopyTo(ms);
myStream.Position = ms.Position = 0; // !Don't forget this!
//And then read your 'ms' here

Resources