0

I am not sure what is happening here.

When I run my web application locally and click a button to download a file, the file is downloaded fine and Response header as you can see in the attached screenshot where it says local.

But when I publish the application to azure web app. Somehow the download button stops working. I checked the Response Header and you can see the difference.

What would cause this problem? The code is the same? Is there any settings that I should be setting in azure web app in azure portal?

enter image description here

Updated to add code

I have debugged remotely to figure out what is going on as @Amor suggested.

It is so strange that When I debug on my local machine first ExportTo action gets hit which prepares the TempData then Download action gets called once the first action completed with ajax call.

However, this is not the case when I debug remotely. Somehow the ExportTo action never gets called. It directly calls the Download action. As a result the TempData null checking is always null.

But why? Why on earth and how that is possible? Is there something cached somewhere?

I have wiped the content of web application on the remote and re-publish evertyhing to ensure everything is updated. But still no success.

here is the code:

[HttpPost]
    public virtual ActionResult ExportTo(SearchVm searchVm)
    {
        var data = _companyService.GetCompanieBySearchTerm(searchVm).Take(150).ToList();

        string handle = Guid.NewGuid().ToString();
        TempData[handle] = data;
        var fileName = $"C-{handle}.xlsx";
        var locationUrl = Url.Action("Download", new { fileGuid = handle, fileName });

        var downloadUrl = Url.Action("Download");

        return Json(new { success = true, locationUrl, guid = handle, downloadUrl }, JsonRequestBehavior.AllowGet);

    }

        [HttpGet]
    public ActionResult Download(string fileGuid, string fileName)
    {
        if (TempData[fileGuid] != null)
        {
            var fileNameSafe = $"C-{fileGuid}.xlsx";
            var data = TempData[fileGuid] as List<Company>;

            using (MemoryStream ms = new MemoryStream())
            {
                GridViewExtension.WriteXlsx(GetGridSettings(fileNameSafe), data, ms);
                MVCxSpreadsheet mySpreadsheet = new MVCxSpreadsheet();
                ms.Position = 0;
                mySpreadsheet.Open("myDoc", DocumentFormat.Xlsx, () =>
                {
                    return ms;
                });
                mySpreadsheet.Document.Worksheets.Insert(0);
                var image = Server.MapPath("~/images/logo.png");
                var worksheet = mySpreadsheet.Document.Worksheets[0];
                worksheet.Name = "Logo";
                worksheet.Pictures.AddPicture(image, worksheet.Cells[0, 0]);
                byte[] result = mySpreadsheet.SaveCopy(DocumentFormat.Xlsx);
                DocumentManager.CloseDocument("myDoc");
                Response.Clear();

                //Response.AppendHeader("Set-Cookie", "fileDownload=true; path=/");
                Response.ContentType = "application/force-download";
                Response.AddHeader("content-disposition", $"attachment; filename={fileNameSafe}");
                Response.BinaryWrite(result);
                Response.End();
            }
        }

        return new EmptyResult();
    }

here is the javascript:

var exportData = function (urlExport) {
    console.log('Export to link  in searchController: ' + urlExport);
    ExportButton.SetEnabled(false);
    var objData = new Object();
    var filterData = companyFilterData(objData);
    console.log(filterData);
    $.post(urlExport, filterData)
        .done(function (data) {
            console.log(data.locationUrl);
            window.location.href = data.locationUrl;
        });
};

When Export button is clicked exportData function is called:

 var exportToLink = '@Url.Action("ExportTo")';
            console.log('Export to link  in index: '+exportToLink);
            SearchController.exportData(exportToLink);

As I mentioned that this code works perfectly on the local machine. something weird is happening on azure webapp that ExportTo action breakpoint is never gets hit.

I am not sure what else I could change to get the ExportTo action hit?

akd
  • 6,538
  • 16
  • 70
  • 112

1 Answers1

2

Based on the Response Header of Azure Web App, we find that the value of Content-Length is 0. It means that no data has been sent from web app server side.

In ASP.NET MVC, we can response file using following ways.

The first way, send the file which hosted on server. For this way, please check whether the excel file has been uploaded to Azure Web App. You could use Kudu or FTP to the folder to check whether the file is exist.

string fileLocation = Server.MapPath("~/Content/myfile.xlsx");
string contentType = System.Net.Mime.MediaTypeNames.Application.Octet;
string fileName = "file.xlsx";
return File(fileLocation, contentType, fileName);

The second way, we can read the file from any location(database, server or azure storage) and send the file content to client side. For this way, please check whether the file has been read successfully. You can remote debug your azure web app to check whether the file content hasn't been read in the right way.

byte[] fileContent = GetFileContent();
string contentType = System.Net.Mime.MediaTypeNames.Application.Octet;
string fileName = "file.xlsx";
return File(fileContent, contentType, fileName);

5/27/2017 Update

Somehow the ExportTo action never gets called. It directly calls the Download action. As a result the TempData null checking is always null.

How many instances does your Web App assigned? If your Web App have multi instances, the ExportTo request is handled by one instance and the Download request is handled by another instance. Since the TempData is store in memory of dedicated instance, it can't be got from another instance. According to the remote debug document. I find out the reason why the ExportTo action never gets called.

If you do have multiple web server instances, when you attach to the debugger you'll get a random instance, and you have no way to ensure that subsequent browser requests will go to that instance.

To solve this issue, I suggest you response the data directly from the ExportTo action or save the temp data in Azure blob storage which can't be accessed from multi instances.

Amor
  • 8,325
  • 2
  • 19
  • 21
  • Thank you @Amor for your answer. I have updated my question to add the code. I debugged on azure web app remotely and I can see one issue as I detailed in the question. Any idea why this is happening? – akd May 26 '17 at 12:18
  • I was not aware of any instances that was created by azure for a web application. I think you have saved me on this. I had disabled the ARR Affinity for the web application. Now it all makes more sense. So if you disable the ARR affinity as you stated that debugging is pretty much useless if you have an application that is not fully stateless such as having tempdata etc. But someone should write a blog about this terrific idea to warn people. Coming back to the issue, It is not possible to response the data from ExportTo action as it is called by an ajax call to pass filter data. – akd May 29 '17 at 17:15
  • Easy solution seems to enable ARR affinity in azure portal for the application. The other option probably is not use use ajax and response the file back to user from ExportTo action. But it looks like a bit of work is required to keep the user on the same page. Thanks for your help – akd May 29 '17 at 17:18
  • Yes, enabling ARR affinity will be an easier solution. However, we would better to make our web application stateless. Saving the temp data to Azure Blob Storage will make you web application stateless and solve this issue. Please try the ways which pointed out from this thread. If one of them help you solve the issue, I suggest you mark this reply as answer. A thread which has been marked will be easily searched. It will help others who encounter similar issues. – Amor May 29 '17 at 18:33