.xlsx file corrumption following streaming event - c#

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());

Related

Generated Docx File is Corrupt - 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).

Is it possible to open a word document from a particular location in C#

Is it possible to open a word document from a particular location in C# using:
string str2 = "Docname.doc"
Response.AppendHeader("Content-Type", "application/msword");
Response.AppendHeader("Content-disposition", "attachment; filename=" + str2);
My problem is that above code create and open a word doc but i want to open any existing doc file at any particular location.
An HTTP response can't contain a reference to a file path on the client computer, if that's what you're after then the answer is no.
If the file is on the server and you want it to open on the client, then you need to read the entire contents of the file and write those contents to the response.
You have to read the document and write it to response as a MemoryStream. I think this should work:
var fileStream = System.IO.File.ReadAllBytes(#"path/to/document.doc");
var stream = new MemoryStream(fileStream);
stream.WriteTo(Response.OutputStream);
Response.AddHeader("Content-Disposition","Attachment;filename=documentName.doc");
Response.ContentType = "application/msword";

How to make a real XLS file on asp.net c#?

I have this code to extract a table to computer in a .xls file:
// I have a string which contains HTML table codes, named as excelTable
HttpResponse response = HttpContext.Current.Response;
response.Clear();
response.AddHeader("Content-Disposition", String.Format("Attachment;Filename=file.xls", ));
response.Buffer = true;
response.ContentEncoding = System.Text.Encoding.Default;
response.ContentType = "application/vnd.ms-excel";
response.Write(excelTable);
response.End();
And I have seen that this is not a real .xls file. I can open it in Notepad and see my standart html table codes.
Now I understand that defining ContentType is not enough. So what else can I do to generate a pure .xls or .xlsx file? Or Should I certainly use Excel Libraries like OpenXML, Interop, etc. ?
Actual XLS/XLSX data must be sent back - that is, data generated with a library (e.g EEPlus) or other suitable source.
This common hack/approach works because Excel "knows how to read HTML". It doesn't actually turn the HTML into XSL/XLSX, although it can be saved as new spreadsheet once loaded by Excel.
Changing the content type will have no effect on the data, and instead will effectively stop this approach from working: the content type is used for associating the program (i.e. Excel) which will be used to open/read the data.
So yes: to generate a real XLS/XLSX document, use a library.

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.

How do I generate and send a .zip file to a user in C# ASP.NET?

I need to construct and send a zip to a user.
I've seen examples doing one or the other, but not both, and am curious if there are any 'best practices' or anything.
Sorry for the confusion. I'm going to generating the zip on the fly for the web user, and sending it to them in the HTTP response. Not in an email.
Mark
I would second the vote for SharpZipLib to create the Zip file. Then you'll want to append a response header to the output to force the download dialog.
http://aspalliance.com/259
should give you a good starting point to achieve that. You basically need to add a response header, set the content type and write the file to the output stream:
Response.AppendHeader( "content-disposition", "attachment; filename=" + name );
Response.ContentType = "application/zip";
Response.WriteFile(pathToFile);
That last line could be changed to a Response.Write(filecontents) if you don't want to save to a temp file.
DotNetZip lets you do this easily, without ever writing to a disk file on the server. You can write a zip archive directly out to the Response stream, which will cause the download dialog to pop on the browser.
Example ASP.NET code for DotNetZip
More example ASP.NET code for DotNetZip
snip:
Response.Clear();
Response.BufferOutput = false; // false = stream immediately
System.Web.HttpContext c= System.Web.HttpContext.Current;
String ReadmeText= String.Format("README.TXT\n\nHello!\n\n" +
"This is text for a readme.");
string archiveName= String.Format("archive-{0}.zip",
DateTime.Now.ToString("yyyy-MMM-dd-HHmmss"));
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", "filename=" + archiveName);
using (ZipFile zip = new ZipFile())
{
zip.AddFiles(f, "files");
zip.AddFileFromString("Readme.txt", "", ReadmeText);
zip.Save(Response.OutputStream);
}
Response.Close();
or in VB.NET:
Response.Clear
Response.BufferOutput= false
Dim ReadmeText As String= "README.TXT\n\nHello!\n\n" & _
"This is a zip file that was generated in ASP.NET"
Dim archiveName as String= String.Format("archive-{0}.zip", _
DateTime.Now.ToString("yyyy-MMM-dd-HHmmss"))
Response.ContentType = "application/zip"
Response.AddHeader("content-disposition", "filename=" + archiveName)
Using zip as new ZipFile()
zip.AddEntry("Readme.txt", "", ReadmeText, Encoding.Default)
'' filesToInclude is a string[] or List<String>
zip.AddFiles(filesToInclude, "files")
zip.Save(Response.OutputStream)
End Using
Response.Close
I'm sure others will recommend SharpZipLib
How do you intend to "send" it. .NET has built in Libraries for email via SMTP
EDIT
In that case you'll want to capture the output stream from SharpZipLib and write it directly to the Response. Just make sure you have the correct Mimetype set in the Response Headers (application/zip) and make sure you don't Response.Write anything else to the user.
Agree with above, SharpZipLib , for creating .zip files in .NET, it seems a very popular option.
As for 'send'ing, if you mean via SMTP/Email, you will need to use the System.Net.Mail name space. The System.Net.Mail.Attachment Class documentation has an example of how to send a file via email
Scratch the above, by the time I posted this I see you meant return via HTTP Response.
One concern is the size of the file that you will be streaming to the client. If you use SharpZipLib to build the ZIP in-memory, you don't have any temp files to cleanup, but you'll soon run into memory issues if the files are large and a number of concurrent users are downloading files. (We experienced this pretty frequently when ZIP sizes got to the 200 MB+ range.) I worked around this by the temp file to disk, streaming it to the user, and deleting it when then request completed.
DotNetZip creates the stream without saving any resources on the server, so you don't have to remember to erase anything. As I said before, its fast and intuitive coding, with an efficient implementation.
Moshe
I modified some codes as below, i use System.IO.Compression
public static void Zip(HttpResponse Response, HttpServerUtility Server, string[] pathes)
{
Response.Clear();
Response.BufferOutput = false;
string archiveName = String.Format("archive-{0}.zip",
DateTime.Now.ToString("yyyy-MMM-dd-HHmmss"));
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", "filename=" + archiveName);
var path = Server.MapPath(#"../TempFile/TempFile" + DateTime.Now.Ticks);
if (!Directory.Exists(Server.MapPath(#"../TempFile")))
Directory.CreateDirectory(Server.MapPath(#"../TempFile"));
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
var pathzipfile = Server.MapPath(#"../TempFile/zip_" + DateTime.Now.Ticks + ".zip");
for (int i = 0; i < pathes.Length; i++)
{
string dst = Path.Combine(path, Path.GetFileName(pathes[i]));
File.Copy(pathes[i], dst);
}
if (File.Exists(pathzipfile))
File.Delete(pathzipfile);
ZipFile.CreateFromDirectory(path, pathzipfile);
{
byte[] bytes = File.ReadAllBytes(pathzipfile);
Response.OutputStream.Write(bytes, 0, bytes.Length);
}
Response.Close();
File.Delete(pathzipfile);
Directory.Delete(path, true);
}
this code gets some list containing list of files copy them in temp folder
, create temp zip file then read all bytes of that file and send bytes in response stream, finaly delete temp file and folder

Categories

Resources