I have a website that, after the user clicks an Imagebutton, calls server side code to create an Excel file and downloads it. This process can take about 20 seconds to finish so I'm wanting to change the cursor to wait mode until it finishes. I've followed the guidelines in this post, but it fails to say how I can reset the cursor back to normal once the download is complete.
Below is the last part of my server side code that actually creates the download, is there something I can possibly do to check the HTTPContext to see if it's finished?
//*****************************************
//* Workbook Download & Cleanup
//*****************************************
using (var stream = new MemoryStream())
{
Response.Clear();
wb.Write(stream);
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", "Student Experiences.xlsx"));
Response.BinaryWrite(stream.ToArray());
Response.Flush();
Response.End();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
I have a website that, after the user clicks an Imagebutton, calls server side code to create an Excel file and downloads it. This process can take about 20 seconds to finish so I'm wanting to change the cursor to wait mode until it finishes. I've followed the guidelines in this post, but it fails to say how I can reset the cursor back to normal once the download is complete.
Below is the last part of my server side code that actually creates the download, is there something I can possibly do to check the HTTPContext to see if it's finished?
//*****************************************
//* Workbook Download & Cleanup
//*****************************************
using (var stream = new MemoryStream())
{
Response.Clear();
wb.Write(stream);
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", "Student Experiences.xlsx"));
Response.BinaryWrite(stream.ToArray());
Response.Flush();
Response.End();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
Share
Improve this question
asked Mar 31 at 20:55
JimmyGJimmyG
6277 silver badges25 bronze badges
11
- Well, actually, the problem is you are downloading a file, and that is a server response, and as such, then you can ONLY return a web page, or ONLY return a file. There's thus not really a provision for any event to run. You don't show how the above server code is being triggered, but regardless, you can't return "both" a server (page) response, and a file at the same time - you can only do one, or the other. In effect, you question is how can I run some client side JavaScript code after downloading a file - that's not a easy task at all. – Albert D. Kallal Commented Apr 1 at 1:10
- Thanks, for clarity the trigger is an asp ImageButton: <asp:ImageButton ID="expExcel" runat="server" AlternateText="Excel" ImageUrl="/DMC/Images/excelDownload2.png" CssClass="excel-img" OnClick="exportExcel" OnClientClick="hourglass(); return true;"/> – JimmyG Commented Apr 1 at 3:38
- Also, I know I can make this work using cookies -- I've done that before: in javascript 1) set the cursor status; look for the existence of a cookie; when cookie is found reset cursor status; and then on the server side once the file has finished downloading create the cookie the js is looking for. But I'm trying to get away from using cookies. – JimmyG Commented Apr 1 at 3:40
- 1 How you going to create cookies after the file download? You may well be able to include a cookie in the response headers before you send the file, but that means the cookie created at start/before the file starts to flow.... – Albert D. Kallal Commented Apr 1 at 4:27
- What kind of website? You only revealed some backend code, which is ASP.NET based, but what's the technology you use to build the front end? Mature frameworks like Angular/React/Blazor all have existing solutions for such scenarios. – Lex Li Commented Apr 1 at 7:31
1 Answer
Reset to default 1Sorry my question didn't contain more detail, I'll try to include all the relevant information in my answer below. I decided to go back to using a cookie. I knew this method works but I was trying to do away with cookies. This is for an old asp webform written with a c# base.
The process starts when somebody clicks on the Excel image:
<img src='/DMC/Images/excelDownload2.png' class="excel-img" onclick="beginDownload('excel');" />
This calls a javascript function that starts looking for a not-yet-created cookie:
function beginDownload(dt) {
$('#progressModal').modal('show');
var downloadFrame = document.createElement("IFRAME");
if (downloadFrame != null) {
downloadFrame.setAttribute("src", 'aolStudentExperiences_Academy.aspx?exportType=' + dt);
downloadFrame.style.width = "0px";
downloadFrame.style.height = "0px";
document.body.appendChild(downloadFrame);
}
var myDownload = setInterval(function () {
if (Cookies.get('studentExperiencesByAcademy') == null) {
}
else {
Cookies.remove('studentExperiencesByAcademy');
$('#progressModal').modal('hide');
clearInterval(myDownload);
}
}, 1000);
}
The modal being called displays a 'please wait' image until the cookie is found:
<!-- Modal -->
<div class="modal fade" id="progressModal" tabindex="-1" role="dialog"
aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="progress" style="height: 25px">
<div class="progress-bar progress-bar-striped progress-bar-animated bg-warning"
role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100"
style="width: 75%; color: black">
...Building File (about 20-25 seconds)
</div>
</div>
</div>
</div>
</div>
The javascript function also builds an iframe and recalls this page with an exportType attribute appended onto the address. When the iframe gets loaded the server side code kicks in:
using DMC.Classes;
using DMC.AOL.WebServices;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
namespace DMC.AOL
{
public partial class aolStudentExperiences_Academy : System.Web.UI.Page
{
int pageID = 134;
export dmc = new export();
string exportType { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//Check to see if this is an excel download
exportType = Request.QueryString["exportType"];
if (exportType == "excel")
{
//Create the cookie to let main page know download is complete
HttpCookie myCookie = dmc.createDownloadCookie("studentExperiencesByAcademy");
//Add the cookie
Response.Cookies.Add(myCookie);
exportExcel();
}
}
}
protected void exportExcel()
{
//Steps to build the Excel file
//*****************************************
//* Workbook Download & Cleanup
//*****************************************
using (var stream = new MemoryStream())
{
//Create the cookie to let main page know download is complete
HttpCookie myCookie = dmc.createDownloadCookie("studentExperiencesbyAcademy");
//Add the cookie
Response.Cookies.Add(myCookie);
Response.Clear();
wb.Write(stream);
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", "Student Experiences.xlsx"));
Response.BinaryWrite(stream.ToArray());
Response.Flush();
Response.End();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
}
}
The createDownloadCookie function is a class:
public HttpCookie createDownloadCookie(string cookieName)
{
//Create a cookie to let the main page know the download has completed
HttpCookie myCookie = new HttpCookie(cookieName);
DateTime now = DateTime.Now;
//Set the cookie value
myCookie.Value = now.ToString();
//Set the cookie expiration date
myCookie.Expires = now.AddMinutes(1);
return myCookie;
}
Once the javascript functino sees the cookie has been created it immediately removes it and hides the modal, and the file is ready to view!