Generated Docx File is Corrupt - C# - c#

I am creating a docx file with my app, and I get stuck with the Response.End(). I get this error:
Thread was being aborted
I get this error, but the file is still created anyway. When I try to open the file it's always corrupted. I am not having any success writing the .docx file. Please let me know what I am doing wrong.
HttpContext.Current.Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=utf-8";
HttpContext.Current.Response.AddHeader("content-disposition", String.Format("attachment;filename={0}", "mydoc.docx"));
HttpContext.Current.Response.BinaryWrite(ourString);
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.End();

Note, you should not place Response.End inside the try-catch block because it's expected that it will throw that exception.
See the HttpResponse.End method's remarks:
To mimic the behavior of the End method in ASP, this method tries to
raise a ThreadAbortException exception.
You can avoid this by using the following:
var response = HttpContext.Current.Response;
response.Clear();
response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document;
response.AddHeader("Content-Disposition", "attachment;filename=mydoc.docx");
response.OutputStream.Write(ourString, 0, ourString.Length);
response.Flush();
HttpContext.Current.ApplicationInstance.CompleteRequest();
Note, in the above code I'm presuming that your ourString variable is byte array because you were passing it to the BinaryWrite method in your snippet code.
However, this name leads me to believe that you have just converted your string to byte[], is that correct?
If it is, note that this is not a valid DOCX file, DOCX format is not a plain text, you need to write it correctly using Office Open XML format (WordprocessingML).

Related

Cannot Send File in Response

I have a code set that runs on the server, which correctly generates a zip file and stores it on the server. I have that file location as a physical path.
Nothing I have attempted has allowed me to use the response to the client to download that file.
Attempt 1:
System.IO.FileInfo fi = new System.IO.FileInfo(zipFilePath);
//setup HTML Download of provided Zip.
//application/zip
Response.ClearContent();
Response.Clear();
Response.ClearHeaders();
Response.Buffer = true;
Response.ContentType = "application / zip";
Response.AddHeader("Content-Disposition",
"attachment; filename=\"" + System.IO.Path.GetFileName(zipFilePath) + "\";");
Response.AddHeader("Content-Length", fi.Length.ToString());
Response.TransmitFile(zipFilePath);
Response.Flush();
Response.End();
No result. Code executes without error but there is no download to the client.
Attempt 2:
//Almost the same as attempt 1, but with WriteFile instead
Response.WriteFile(zipFilePath);
No Result, same as Attempt 1.
Attempt 3:
//Note: Same Header Section as Attempts 1 and 2
System.IO.BinaryReader reader = new System.IO.BinaryReader(new System.IO.FileStream(zipFilePath, System.IO.FileMode.Open));
int CHUNK = 1024;
List<byte> FileArray = new List<byte>();
while (reader.BaseStream.Position < reader.BaseStream.Length)
FileArray.AddRange(reader.ReadBytes(CHUNK));
byte[] bArray = FileArray.ToArray();
reader.Close();
Response.OutputStream.Write(bArray, 0, bArray.Length);
Response.Flush();
Response.End();
No Result, Same as previous attempts
Attempt 4:
//Identical to Attempt 3, but using BinaryWrite
Response.BinaryWrite(bArray);
No Result, Same as previous Attempts.
The Question
Every one of these code blocks runs with no error, But The Save File dialog NEVER appears. I get nothing at all. I cannot figure out for the life of me what I might be missing.
The File Path has been verified as correct
The Code is running on the server, not on the client, I cannot use the 'WebClient.Download' method for this reason
If anyone has any suggestions, I'm all ears. I have no idea how to get this file to download to the client.
I tested your code (attempt 1) and got it working fine with a test file. If the file path would be wrong, you'd get an System.IO.FileNotFoundException so that's probably not the issue.
A couple of ways to address this:
Try inspecting the webpage in, for example, Chrome by right-clicking
and choose inspect. Then click on Network tab, and refresh the
page (where you're supposed to get the file). Check the response
headers for that request - what is it?
Try setting content-type to application/octet-stream
Use debugger in Visual Studio and step through.
This turned out to be an Ajax related error causing issues between UpdatePanels and POST Responses.
The issue was fixed on the page load of the page by adding the call
ScriptManager.GetCurrent(Page).RegisterPostBackControl(btnGenerate);

Downloading a file, can you cancel the response when an error occurs, without buffering the content

I am writing large CSV files to the Response with Response.BufferOutput set to false. This is because the file has the potential to take a long time to download so the user can see some progress. I generate a line for the CSV from a object and write to the response using Response.Write().
This works well however if there is an unexpected error after the Response has started writing then the client will receive a file with only part of the data and could be missing lots of lines but they might not realise it.
Is there a way to somehow cancel the file download without buffering all the content? Could there be some way to indicate that the response is invalid so the browser disregards the file?
Code below shows the main idea of my code
public void StreamCsvFile(string fileName,List<myObject> myObjectList)
{
Response.Clear();
Response.ContentType = "text/csv";
Response.AddHeader("content-disposition", "filename=" + fileName);
Response.BufferOutput = false;
string headerLine = GetHeaderLine();
Response.Write(headerLine)
try
{
foreach(var myObject in myObjectList)
{
string line = myObject.ToCsvString();
Response.Write("\n" + line);
}
}
finally
{
Response.End();
}
}
Write to a temporary file first.
Set the content-length on the response.
Use Response.TransmitFile to send this temp file.
Browsers will reject download if content length doesn't match.

PDF won't display in browser window

I am using a handler to get and display a PDF in the browser window using the code below:
byte[] byt = RetrieveDocument(int.Parse(context.Request.Params["id"]), context.Request.Params["title"]);
string file = WriteDocumentFilePDF(byt);
HttpContext.Current.Response.ContentType = "application/pdf";
HttpContext.Current.Response.AddHeader("content-length", byt.Length.ToString());
HttpContext.Current.Response.AddHeader("Content-Disposition", "inline; filename=programdetails.pdf");
HttpContext.Current.Response.BinaryWrite(byt);
HttpContext.Current.Response.End();
The function WriteDocumentFilePDF successfully writes the PDF to the temp directory. I have the above code working correctly in a different application. Am I missing something?
When debugging issues like this, I find that Fiddler is an invaluable tool; many many times it has saved me from simple mistakes. Also, this site http://www.c-sharpcorner.com/uploadfile/prathore/what-is-an-ashx-file-handler-or-web-handler/ gives an example of doing the same thing but with a GIF image. The difference between your example and his seems to be the use of Response.WriteFile() rather than the direct write to the Response using BinaryWrite().
I would perform a Response.ClearHeaders() before I set the content-type and I would remove the call to Response.End().
Does it make a difference if you pass the byte[] to memorystream first? So something like
byte[] byt = RetrieveDocument(int.Parse(context.Request.Params["id"]), context.Request.Params["title"]);
string file = WriteDocumentFilePDF(byt);
MemoryStream ms = new MemoryStream(byt);
And then add your headers
HttpContext.Current.Response.ContentType = "application/pdf";
HttpContext.Current.Response.AddHeader("Content-Disposition", "inline; filename=programdetails.pdf");
HttpContext.Current.Response.BinaryWrite(ms.ToArray());
HttpContext.Current.Response.End();

.xlsx file corrumption following streaming event

I am using the EPPlus library to create an .xlsx file which I am then streaming to the browser. To stream the code I am using:
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.Buffer = true;
HttpContext.Current.Response.ContentType = "application/octet-stream";
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + Server.MapPath("xls/"+ download_results_filename));
HttpContext.Current.Response.TransmitFile(Server.MapPath("xls/" + download_results_filename));
HttpContext.Current.Response.Flush();
My question is this. If I use my code along with the library, save and open the document everything is fine. However, when I create the file on the server and stream it to the user, using the code above, I get a corrupt file message with the additional ability to correct or recover the file, which I do and the file displays correct; so if using the octet stream method above to stream the file to the user corrupts the file how should I stream the Binary data to the user. I want to keep the content type to 'application/octet-stream' as if I am specific about it being an excel spreadsheet I run into problems on the iPad.
Thanks
Did you see this example?
http://epplus.codeplex.com/wikipage?title=WebapplicationExample
It use this line
Response.BinaryWrite(pck.GetAsByteArray());

Can I download an Excel file made from a memory stream off an ASP.NET page?

I have an ASP.NET page where a user provides an ID, and then we pull some data from the DB and put it into an Excel spreadsheet. I would like to create the Excel file in memory and then allow the user to download the file. I could create a file on the server, and then delete it afterwards, but it seems unnecessary. Depending on error handling I could potentially orphan a file with that approach, etc.
Is something like this possible? Or do I need to use a file stream?
On a side note, I'm using EPPlus as an API (quick plug for it).
You want to specify the content-type and content-dispisition headers like so - Response.ContentType = "application/vnd.ms-excel" works in IE and firefox but not in Safari, then stream your file. Once complete, call Response.End() to stop the application execution
Code Sample:
void StreamExcelFile(byte[] bytes)
{
Response.Clear();
Response.ContentType = "application/force-download";
Response.AddHeader("content-disposition", "attachment; filename=name_you_file.xls");
Response.BinaryWrite(bytes);
Response.End();
}
ExcelPackage pck = new ExcelPackage();
.....
.....
.....
byte[] bfr = pck.GetAsByteArray();
Response.ContentType = "application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AppendHeader("content-disposition", "attachment; filename=ExcelFileName.xlsx");
Response.OutputStream.Write(bfr, 0, bfr.Length);
Response.Flush();
Response.Close();
Yes, look into using an HTTP Handler to stream the file to the browser from memory.
http://msdn.microsoft.com/en-us/library/ms972953.aspx
What you're looking for is called a generic handler:
http://www.dotnetperls.com/ashx
In a nutshell, what you need to do is define the context type. Which in your case will be a xls or xlsx. Set the response, and direct the user to the handler.
They will be prompted to download the file.

Categories

Resources