How to handle errors when using ASP.NET to create a zipfile for download? - asp.net

I'm working on a functionality in my asp.net web site that enables the user to download some files as a zip file. I'm using the DotNetZip library to generate the zip file.
My code looks like this:
protected void OkbtnZipExport_OnClickEvent(object sender, EventArgs e)
{
var selectedDocumentIds = GetSelectedDocIds();
string archiveName = String.Format("archive-{0}.zip", DateTime.Now.ToString("yyyy-MMM-dd-HHmmss"));
AddResponseDataForZipFile(Response, archiveName);
try
{
string errorMessage = Utils.ExportToZip(selectedDocumentIds, arkivdelSearchControl.GetbraArkivConnection(), Response.OutputStream);
if (!string.IsNullOrEmpty(errorMessage))
{
LiteralExportStatus.Text = errorMessage;
}
else
LiteralExportStatus.Text = "Success";
}
catch (Exception ex)
{
LiteralExportStatus.Text = "Failure " + ex.Message;
}
Response.Flush();
Response.Close();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
private void AddResponseDataForZipFile(HttpResponse response, string zipName)
{
Response.Clear();
Response.BufferOutput = false;
Response.ContentType = "application/x-zip-compressed";
Response.AddHeader("content-disposition", "attachment; filename=" + zipName);
Response.AddHeader("Expires", "0");
Response.AddHeader("Content-Description", "Zip Arcive");
}
Now, if anything goes wrong, say the Utils.ExportToZip method fails, I want to present an error message to the user and not the download dialog. Do I have to remove some data from the Response object in order to cancel the download operation?
Best regards
OKB

first, Don't call HttpContext.Current.ApplicationInstance.CompleteRequest();
Reference.
At one point, there was some example code that showed CompleteRequest(), but it's wrong.
Second - to do what you describe,
you'll need to insure that the zip file can be created correctly and in its entirety, before sending anything. That means you should do the AddResponseDataForZipFile() only after the zipfile is completely created. That means you need to create an actual zip file on the server, and not simply save out to Response.OutputStream. Once the file is successfully created, then call AddResponseDataForZipFile(), stream the bytes for the temp zip file, call Response.Close(), then delete the temporary zip file.

I can't comment at the moment, so take this answer as one.
How does Utils.ExportToZip work?
If the reason it takes the Response.OutputStream for the constructor is to write the zip-file directly into it, then you need to set Buffering in order to "undo" that in your AddResponseDataForZipFile Method:
Response.BufferOutput = true;

Related

MVC 4 Export To CSV - Save As dialogue box not working in Chrome and Firefox

I am trying Export csv file to the User with Open/Save option.
My issue is some what similar to how-to-force-chrome-to-open-an-open-file-dialog-when-downloading-a-file-via-as(It is downloading the file in Chrome and Firefox), I have tried with the solution suggested by #Dev but it is not working.
I wrote my code as below:-
return File(new System.Text.UTF8Encoding().GetBytes(csvData),
"text/csv", filename);
But, it was not working in Chrome. The file is getting downloaded by default.
Then after googling , I found returning-a-file-to-view-download-in-mvc, from which I was trying to do something like below:-
var csvData = "hello";// I am filling this variable with ,y values from DB!
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = "test",
Inline = false,
};
Response.AppendHeader("Content-Disposition",
cd.ToString());
return File(new System.Text.UTF8Encoding().GetBytes(csvData),
"text/csv");
but still it was downloading the file in Chrome. then I came across how-to-display-open-save-dialog-asp-net-mvc-4, where #JoãoSimões mentioned as:-
That is browser dependent. If you set to download automatically to a
given folder, the browser will download automatically. Firefox and
Chrome are some browsers with this behavior. – João Simões Jan 3 at
13:09
If the above is true, then how can I overcome my problem? How can I get the open/save dialogue ?
I am unable to Export my CSV with open/save option.
Edit 1
I was trying to do something like this (got it here):-
public class ExcelResult : ActionResult
{
public string FileName { get; set; }
public string Path { get; set; }
public string Data { get; set; }
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Buffer = true;
context.HttpContext.Response.Clear();
context.HttpContext.Response.AddHeader("content-disposition", "attachment; filename=" + FileName);
context.HttpContext.Response.ContentType = "text/csv";
context.HttpContext.Response.Write(new System.Text.UTF8Encoding().GetBytes(Data));
}
}
and My controller code:-
return new ExcelResult
{
FileName = "sample.xls",
Path = "",
Data = csvData
};
but still, it is downloading the Excel ...
Edit 2
Tried opening the excel with HttpContext.Current.Response
/// <summary>
/// Export CSV
/// </summary>
/// <returns></returns>
public void DownloadCSV()
{
try
{
var csvData = Session["CSVData"].ToString();
byte[] getContent = new System.Text.UTF8Encoding().GetBytes(csvData);
System.Web.HttpContext.Current.Response.ClearContent();
System.Web.HttpContext.Current.Response.ClearHeaders();
System.Web.HttpContext.Current.Response.Buffer = true;
System.Web.HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
System.Web.HttpContext.Current.Response.AddHeader("Content-Length", getContent.Length.ToString());
System.Web.HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=" + "testing.csv");
System.Web.HttpContext.Current.Response.BinaryWrite(getContent);
System.Web.HttpContext.Current.Response.Flush();
}
catch (Exception ex)
{
HttpResponseMessage message = new HttpResponseMessage()
{
Content = new StringContent("Error Exporting Data")
};
throw new System.Web.Http.HttpResponseException(message);
}
}
but, still not working!!!
#shubh have you tried How to force Chrome to open an "open file dialog" when downloading a file vía ASP.NET codebehind? second solution they have put image in where they show how to open dialog box in chrome. I have chrome Version 30.0.1599.101 m in that if you go to setting in that advance setting then down you will find check box which was given in above link answer, that will solve your problem I think.
If still not working then might be problem with your browser just update it to latest version then try again.
Edit 1:
If you put your file name extension .xls then it will open in excel for csv you need to put file name as FileName = "sample.csv", then it will open in csv format.
Response.Clear();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment;filename=GridViewtoCSVExport.csv");
Response.Charset = string.Empty;
Response.ContentType = "application/text";
for more check this http://surajdeshpande.wordpress.com/2013/09/03/export-gridview-data-to-csv-file-in-asp-net/
If the user has configured his browser to automatically download files, there's absolutely nothing you could do on the server to force this dialog to appear. I am afraid that what you are trying to achieve is impossible.
Try to supply another value for your content-disposition header:
Response.AppendHeader("Content-Disposition", "attachment");

Create Text File in Memory and return it over ajax, possible?

I can create a text file easily enough but I want to avoid having to keep the file on the server.
How can I create a text file in memory and return it over ajax so the file itself is returned and no file is kept on server? It doesn't need to be ajax but I want to avoid a postback if at all possible.
You can use below code to download text file in webforms
MemoryStream ms = new MemoryStream();
TextWriter tw = new StreamWriter(ms);
tw.WriteLine("HELLO WORLD!");
tw.WriteLine("I WANT TO SAVE THIS FILE AS A .TXT FILE!");
tw.Flush();
var bytes = ms.GetBuffer();
Response.ClearContent();
Response.AddHeader("Content-Disposition", "attachment; filename=test.txt");
Response.AddHeader("Content-Length", bytes.Length.ToString());
Response.ContentType = "text/plain";
Response.BinaryWrite(bytes);
Vijay's answer is correct if you're using either MVC or WebForms.
About preventing postback, You don't have to use ajax to prevent postback.
If you're using MVC, You just have to use window.location and point it to your action method in js. Something like:
// In your controller:
public class HomeController : Controller
{
public FileResult GetFile2(int id)
{
if (SomeCondition)
{
return null;
}
var fileName = "MyResult.txt";
var content = "Here's the result";
var contentType = "text/plain";
return File(Encoding.ASCII.GetBytes(content), contentType, fileName);
}
// And in your view/js file:
window.location.href = ('/Home/GetFile?id=1');
And if you're using webforms, I think best way is to create a HttpHandler to handle download links. A good tutorial can be found here.

Response.Redirect is not working

i am writing a code where after clicking one button one page will be redirected to another nd after opening 2nd page one pdf file will be download just like this website http://www.findwhitepapers.com/content22881 .But instead of opening the 2nd page and downloading the pdf file only pdf file is downloaded but 2nd page is not opening.1st page code is
protected void Button1_Click(object sender, EventArgs e)
{
Response.Redirect("2nd.aspx");
}
2nd page's code is written below.
protected void Page_Load(object sender, EventArgs e)
{
string filepath = "guar-gum/Guar-gum-export-report.pdf";
// The file name used to save the file to the client's system..
string filename = Path.GetFileName(filepath);
System.IO.Stream stream = null;
try
{
// Open the file into a stream.
stream = new FileStream(Server.MapPath("Guar-gum-export-report.pdf"), System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read);
// Total bytes to read:
long bytesToRead = stream.Length;
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
// Read the bytes from the stream in small portions.
while (bytesToRead > 0)
{
// Make sure the client is still connected.
if (Response.IsClientConnected)
{
// Read the data into the buffer and write into the
// output stream.
byte[] buffer = new Byte[10000];
int length = stream.Read(buffer, 0, 10000);
Response.OutputStream.Write(buffer, 0, length);
Response.Flush();
// We have already read some bytes.. need to read
// only the remaining.
bytesToRead = bytesToRead - length;
}
else
{
// Get out of the loop, if user is not connected anymore..
bytesToRead = -1;
}
}
Response.Flush();
}
catch (Exception ex)
{
Response.Write(ex.Message);
// An error occurred..
}
finally
{
if (stream != null)
{
stream.Close();
//
}
}
}
What do you expect to see on the 2nd page? All you have there is a pdf file. Do you expect an empty page?
Your redirect works fine. When you click the button a PDF file will be sent back to the browser and it will see it as a file that should be downloaded. No page will be sent to the browser so there is no page to see.
Here is the solution:
Don't code what you have done in page2.aspx for file downloading instead put an iframe in the page2.aspx and set the src to the file Url.
I guess it is guar-gum/Guar-gum-export-report.pdf in your case. May be you should change this to start from root of the site by prefix / to the file url.
Put this in page2.aspx
<iframe width="1" height="1" frameborder="0" src="[File location]"></iframe>
It is very simple way and No redirects or JavaScript and your Page2.aspx will also open.
UPDATE
Based on the comments below this answer
I think there is no better solution but here is another mindbending one (psst! hope you and other like..) move the page2.aspx code one for file download ONLY to the third page page3.aspx and set iframe.src to page3.aspx
Reference
SO - How to start automatic download of a file in Internet Explorer

Any info on the chinese '360 secure browser'? Having issues with it downloading files I am streaming

There is a desktop browser called '360 secure browser'. They have a fairly large share of the market in China, and we are required to support them.
It says the layout engine is Trident (IE), which is what I expected, but I can't verify that right now (on a mac!).
The reason for this is that I have some forms that kick off a download, streaming bytes to the client, and they work in the other major browsers. The code that causes the issue is below, or similar. Is this doing something wrong that I don't notice? The byte streams are usually on the order of 50-100KB, and we haven't had issues with it yet.
This code is called in response to a PostBack event (eg, button click in a grid, etc)
This function is called with bytestreams from files, generated in memory, or read from db.
The function:
public static bool DownloadStream(byte[] packageStream, string fileName) {
var response = HttpContext.Current.Response;
response.Clear();
response.AddHeader("Accept-Ranges", "bytes");
response.AddHeader("Content-Disposition", "inline; filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8));
response.AddHeader("Content-Length", packageStream.Length.ToString());
response.ContentType = "application/xlsx";
response.BinaryWrite(packageStream);
response.Flush();
HttpContext.Current.ApplicationInstance.CompleteRequest();
return true;
}
Does anyone have any experience supporting this browser? I can't find any information on it when searching in english on google. No specs, no docs, nothing. I have to go to Baidu to find info, and I can't read that level of chinese!
EDIT:
The issue is with the downloader that 360 uses, apparently. I would like to know if there is something that should be changed in the streaming code, though. A header that I am missing, or something else.
This is only happening for small files. Same page, bigger download = no issues.
Changing to the built-in IE downloader causes the issue to go away.
Hi i tried your code on 360 secure browser. it work for me. and i edit a little bit below is my code.
Note: As i know, 360 secure browser is using IE Core.
protected void Page_Load(object sender, EventArgs e)
{
DownloadStream(StreamFile(#"C:\Users\My\Desktop\test2.xlsx"), "test.xlsx");
}
private byte[] StreamFile(string filename)
{
FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
// Create a byte array of file stream length
byte[] Data = new byte[fs.Length];
//Read block of bytes from stream into the byte array
fs.Read(Data, 0, System.Convert.ToInt32(fs.Length));
//Close the File Stream
fs.Close();
return Data; //return the byte data
}
public static bool DownloadStream(byte[] packageStream, string fileName)
{
var response = HttpContext.Current.Response;
response.ClearContent();
response.ClearHeaders();
response.AppendHeader("Accept-Ranges", "bytes");
response.AppendHeader("Content-Disposition", "inline; filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8));
response.AppendHeader("Content-Length", packageStream.Length.ToString());
response.ContentType = "application/xlsx";
response.BinaryWrite(packageStream);
response.Flush();
response.End();
return true;
}

ASP.NET show PDF file to user instead of "save as" dialog

My ASP.NET application return PDF file to user using code below
Context.Response.Clear();
Context.Response.ContentType = "application/pdf";
Context.Response.TransmitFile(optionEntityCmd.PathToSave);
Context.Response.End();
This code show Save As browser dialog, is it possible instead of Save As dialog load PDF file directly in browser?
You could append the Content-Disposition header:
Context.Response.AppendHeader(
"Content-Disposition",
"inline; filename=foo.pdf"
);
Is it a dynamically created file? If not, you can just hyperlink or Response.Redirect to it I believe.
I do not know for sure for classic asp.net but using mvc, streaming it to the user does what you want:
MemoryStream stream = PDF.GeneratePDFByStream();
stream.Flush(); //Always catches me out
stream.Position = 0; //Not sure if this is required
return stream;
with
public static MemoryStream GeneratePDFByStream() {
var doc1 = new Document();
//use a variable to let my code fit across the page...
string path = AppDomain.CurrentDomain.BaseDirectory + "PDFs";
MemoryStream stream = new MemoryStream();
PdfWriter writer = PdfWriter.GetInstance(doc1, stream);
writer.CloseStream = false;
// Actual Writing
doc1.Open();
// writing comes here, you will probably just load the PDF in a stream?
doc1.Close();
return stream;
}
And your MVC controller returns something like
return File(GetPDFStream(id), "application/pdf");
So, I know this is not the exact answer you are looking for, but maybe you should try to stream your PDF to the user as it will open it in a new tab as far as I ever tested it.
From the top of my head, you should get something like:
Response.Clear();
Response.ContentType = "application/pdf";
Response.OutputStream.Write( objMemoryStream.ToArray(), 0,
Convert.ToInt32(objMemoryStream.Length));
Response.Flush();
try { Response.End(); } catch{}

Resources