I have the code below which works well for small files but for large files it generates the zip as required but doesn't download it. I get all sorts of errors including Timeout (which I have managed to resolve). The other problem is that it runs in Sync. The largest file I have generated myself is a 330MB zip file with about 30 HD images attached to it. But this can even go to GBs as the user can choose to download about 100 or even more HD images at once.
To resolve both issues, I thought downloading in async may help in both cases. I want to alert the user that their download has started, and that they will be notified when it is ready.
I am thinking of sending the stream down if the client IsConnected (then delete the file) or sending an email to ask them to download the file if they have decided to logout (then delete the file using the offline download link). I just don't know where or how to write async code, or if what I want to do can actually be done if the user decides to logout.
Here's my current code:
private void DownloadFile(string filePath)
{
FileInfo myfile = new FileInfo(filePath);
// Checking if file exists
if (myfile.Exists)
{
// Clear the content of the response
Response.ClearContent();
// Add the file name and attachment, which will force the open/cancel/save dialog box to show, to the header
Response.AddHeader("Content-Disposition", "attachment; filename=" + myfile.Name);
// Add the file size into the response header
Response.AddHeader("Content-Length", myfile.Length.ToString());
// Set the ContentType
Response.ContentType = "application/octet-stream";
Response.TransmitFile(filePath);
Response.Flush();
try
{
myfile.Delete();
}
catch { }
}
}
I don't know about Async downloads from asp.net applications so I can't address that question. But I have run into enough download issues to always start from the same place.
First, download from a generic handle (ASHX) and not a web form. The webform wants to do extra processing at the end of the request that can cause problems. You question didn't state if you are using a web form or generic handler.
Second, always end the request with the ApplicationInstance.CompleteRequest() method call. Don't use Request.Close() or Request.End()
Those two changes have often cleaned up download issues for me. Try these change and see if you get the same results. Even if you do get the same results this is a better way of coding downloads.
Finally, as an aside, only catch appropriate exceptions in the try-catch bock.
Your code would be like this:
public class Handler1 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// set from QueryString
string filePath = "...";
FileInfo myfile = new FileInfo(filePath);
// Checking if file exists
if (myfile.Exists)
{
// Clear the content of the response
context.Response.ClearContent();
// Add the file name and attachment, which will force the open/cancel/save dialog box to show, to the header
context.Response.AddHeader("Content-Disposition", "attachment; filename=" + myfile.Name);
// Add the file size into the response header
context.Response.AddHeader("Content-Length", myfile.Length.ToString());
// Set the ContentType
context.Response.ContentType = "application/octet-stream";
context.Response.TransmitFile(filePath);
context.Response.Flush();
HttpContext.Current.ApplicationInstance.CompleteRequest();
try
{
myfile.Delete();
}
catch (IOException)
{ }
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
Related
I have download code functionality in my ASP.NET project and the download code look like below.
public class Download : IHttpHandler
{
private void DownloadPsListingProduct(Guid which)
{
string path = GetFilePathFromGuid(which);
FileInfo file = new FileInfo(path);
HttpResponse response = HttpContext.Current.Response;
response.ClearContent();
response.Clear();
response.ContentType = "application/octet-stream";
response.AddHeader("Content-Disposition",
"attachment;filename=\"" + file.Name.NeutralizationCrlfSequences() + "\";");
response.TransmitFile(file.FullName);
response.Flush();
response.End();
}
public bool IsReusable
{
get
{
return false;
}
}
}
This code work like a charm when I download single file at a time.
But when one file is under process of downloading and I request to download other file then it first wait to completion of first file downloading and then second file download start.
Note: I am sending new request to download each file.
I want to avoid this single file download behavior and user should able to download files without waiting previous one to complete.
ASP.NET Web API 2 should be able to handle this with very little ceremony. There's an example here, but I'll re-iterate the important parts:
public class FilesController : ApiController
{
public IHttpActionResult(Guid fileId)
{
var filePath = GetFilePathFromGuid(fileId);
var fileName = Path.GetFileName(filePath);
var mimeType = MimeMapping.GetMimeMappting(fileName);
return OkFileDownloadResult(filePath, mimeType, fileName, this);
}
}
Of course, hooking up routing etc in ASP.NET Web API 2 is quite different from hooking up an IHttpHandler, but there's also a plethora of examples on the internet (including here on SO) on how to get started with that.
I have list of PDF files on website (asp.net webforms). i want to open them with Save As option rather than it downlaods directly.
I tried to add download property to the link which didn't work. only was around seems to be HTTPHandler for *.pdf request.
I saw a piece of code for MVC based example here
return new FileStreamResult(stream, "application/pdf")
{
FileDownloadName = "file.pdf"
};
How can i convert this to HTTPHandler in as.net webform so that it open pdf files with Save As option.
I want to do it in a way so that when ever user click on any pdf file at that time Handler should come into action.
OR
I can create another file handlePDF.aspx and write code there also and will change link of pdf file to below
File One
If what you are trying to do is when they click on the file download link it pops up with save as or open dialog box, this is to do with the user's browser configuration. In the case of PDF's i believe Firefox has open in tab as the default option. If you try to push the file as a file stream it will more than likely just load it in a new tab as well.
tl;dr: Client side issue
You're on the right track. Serving PDF files are usually handled by an HttpHandler. That is, unless they can be served straight from the file system by the StaticHandler...
The key thing that is needed in order for the browser to raise the "Open or save" dialog is the Content-Disposition header in the response.
Here is an (untested) implementation that should get you on the right track:
public void ProcessRequest(HttpContext context)
{
string fileName = context.Request.QueryString["file"];
if(fileName == null || fileName == "")
{
throw new ArgumentException("The file argument cannot be null or empty");
}
// fetch file here from db/filesystem/other storage
byte[] fileBytes = LoadFileBytes(fileName);
context.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
context.Response.ContentType = "application/pdf";
context.Response.BinaryWrite(fileBytes);
}
If you want to avoid buffering the whole file in memory, this might also work (requires .Net 4.0 for the CopyTo method on the stream):
public void ProcessRequest(HttpContext context)
{
string fileName = context.Request.QueryString["file"];
if(fileName == null || fileName == "")
{
throw new ArgumentException("The file argument cannot be null or empty");
}
// fetch file stream from db/filesystem/other storage
Stream fileStream = GetFileStream(fileName);
context.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
context.Response.ContentType = "application/pdf";
fileStream.CopyTo(context.Response.OutputStream);
}
I have developed an application where i am getting data from database ,binding it to an Infragistics grid and then downloading excel using its export utility.
There is a problem with this approach when data set is large (say 20000 records or more), it 'll take long time to process and download, and usually it 'll die the page and show blank page to user.
Is there any better approach to handle this issue and make reasonable improvements in excel download process?
Code is like something below:
public void LoadExcelPostingData()
{
try
{
query = "Some complex query here with up to 10 columns";
dt.Clear();
dt = new DataTable();
db2.GetDataTable(query, CommandType.Text, ref dt);
grdJurdata.DataSource = dt;
grdJurdata.DataBind();
ExportToExcel();
}
catch (Exception ex)
{
lblresult.Text = "Grd Err : " + ex.Message;
}
}
private void ExportToExcel()
{
try
{
// Infragistics built in excel export utility
UltraWebGridExcelExporter2.Export(grdJurdata);
}
catch (Exception ex)
{ }
}
Regarding file download Microsoft's MSDN provide a detailed explanation
Get the response
With the response, Set the content type to "APPLICATION/OCTET-STREAM" (it means there's no application to open the file).
Set the header to "Content-Disposition", "attachment; filename=\"" + + "\"".
Write the file content into the response.
Close the response.
Also keep in mind that never use Ajax request to download file because for file transfer , It needs complete PostBack Request Here is the sample code given on MSDN
<%
try
{
System.String filename = "myFile.txt";
// set the http content type to "APPLICATION/OCTET-STREAM
Response.ContentType = "APPLICATION/OCTET-STREAM";
// initialize the http content-disposition header to
// indicate a file attachment with the default filename
// "myFile.txt"
System.String disHeader = "Attachment; Filename=\"" + filename +
"\"";
Response.AppendHeader("Content-Disposition", disHeader);
// transfer the file byte-by-byte to the response object
System.IO.FileInfo fileToDownload = new
System.IO.FileInfo("C:\\downloadJSP\\DownloadConv\\myFile.txt");
Response.Flush();
Response.WriteFile(fileToDownload.FullName);}
catch (System.Exception e)
// file IO errors
{
SupportClass.WriteStackTrace(e, Console.Error);
}
%>
I also suggest you to read this good discussion
Edit #1: Another solution for your case is to
Create a new page to hold the UltraWebGridExcelExporter, and in your main page, create a iframe tag to hold that new page. Let the iframe postback. And also upgrade your Infgraistics version to latest.
First you need to relook at the code how you wrote. you need to refactor or improve the codebase.
if you want to increase the request timeout, then you can do this
you need to add more timeout duration in web.config
<system.web>
<httpruntime executionTimeout="4800"/> //or higher values
</system.web>
I'm getting a file from a database in byte [] format and want user to see download dialog before Linq will take it from the database. It's in C# and ASP.NET.
Now, it's like this:
User choose a file, click on it.
In code I get id of file clicked and using Linq I'm downloading.
Then I send the file by Response.OutputStream.Write(content, 0,
content.Length);
Before a file is downloaded from the database user won't see any
download dialog.
What can I do if I want users to see the download dialog before file is downloaded?
Code:
Getting file by id:
public static byte[] getFile(Guid id)
{
var linqFile = from file in MyDB.Files
where file.IdPliku.Equals(id)
select new
{
Content = file.Content
};
return linqFile.ToList().FirstOrDefault().Content.ToArray();
}
Saving file:
public void SaveFile(Guid fileID, string filename, string mimeTypes)
{
try
{
byte[] content = FileService.getFile(fileID);
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = mimeTypes;
Response.AppendHeader("Accept-Ranges", "bytes");
Response.AppendHeader("Content-Range", string.Format("0-{0}/{1}", content.Length, content.Length));
Response.AppendHeader("Content-Length", content.Length.ToString());
Response.AppendHeader("Content-Encoding", "utf-8");
Response.AppendHeader("Content-Type", Response.ContentType);
Response.AppendHeader("Content-Disposition", "attachment; filename= " + HttpUtility.UrlEncode(filename));
Response.OutputStream.Write(content, 0, content.Length);
//Response.BinaryWrite(content);
Response.Flush();
}
finally
{
Response.Close();
}
}
You are my hope.
your issue is here:
byte[] content = FileService.getFile(fileID);
because in this line you allocate the whole file in the web server's RAM and put everything in there, all content of the file from the database; what happens later does not matter anymore because you have already downloaded from db to web server in this line!!!
I am having such Deja-vu because I am sure I have given exactly the same comment on a very same question few weeks ago. Can't find it now, search for something like this here in SO.
In fact the solution is to stream directly to the output stream of the Response avoiding your byte[] array allocation above, to get this your data layer should of course support it and if it does not you could add a method for this. You want to use SQL Server filestream or something similar.
Greatings!
I'm working on a reporting script which runs a number of reports (pdf) on button click. The reports are created on the web server then I'd like the user to be given the option to download the files. I have worked out the script for downloading one file from the server. But I'm not sure how to download multiple files? (there will probably be about 50)
After I run one report I redirect the user to a http handler script.
Response.Redirect("Download.ashx?ReportName=" + "WeeklySummary.pdf");
public class Download : IHttpHandler {
public void ProcessRequest(HttpContext context)
{
StringBuilder sbSavePath = new StringBuilder();
sbSavePath.Append(DateTime.Now.Day);
sbSavePath.Append("-");
sbSavePath.Append(DateTime.Now.Month);
sbSavePath.Append("-");
sbSavePath.Append(DateTime.Now.Year);
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.ContentType = "application/pdf";
HttpResponse objResponce = context.Response;
String test = HttpContext.Current.Request.QueryString["ReportName"];
HttpContext.Current.Response.AppendHeader("content-disposition", "attachment; filename=" + test);
objResponce.WriteFile(context.Server.MapPath(#"Reports\" + sbSavePath + #"\" + test));
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.End();
}
public bool IsReusable { get { return false; } }
}
Thanks in advance, please let me know if you'd like to see any more of my script.
The 2 options I see right away is the obvious one to simply call the HTTP Handler repeatedly. Another one would be to zip them on the server and send a zip file across the wire. You could use the built in GZipStream class to accomplish this.
Also, you'll want to add some code in your handler to clean up those temp files once they're downloaded.