locked
C# ajax chainin controller actions and/or promises ro get what I need done in 1 button click RRS feed

  • Question

  • User-544325736 posted

    Hello all, I caurrently have a project where you click a button and with this button I have it working that it creates a report that is selected. If multiple reports are selected it creates the multiple reports in a zip file and unzips them all to a specific folder. I have all of this working so far, Now I want to create a new email for each report and attach it to the email. How would  I go about doing this. I can create an email without any problem but attaching the file after doing all these other steps. I been chaining promises with ajax to get multiple controller actions to execute.

            $('#btnGetChkEmail').on('click', function() {
                var arrChkBoxes = [];
                var arrSelectedChks = [];
                var myJSON = {};
                var phaseOne, phaseTwo;
                var isValid = false;
                var bool = false;
                var isZipped = false;
                //var unZipped = false;
    
                // Turn all selected checkbox T/F values into QuoteIDs
                $("input:checked").each(function (index, value) {
                    arrChkBoxes.push($(value).val());
                });
                /* Below i can Create reports / zip / unzip to location and create an email */
    
                if (isValid) {
                    $.ajax({
                        type: "GET",
                        url: "/Service/ExportFiles/",
                        contentType: "application/json; charset=utf-8",
                        traditional: true,
                        data: { "quoteIDs" : arrSelectedChks },
                        success: function () {
                            window.location = '/Service/DownloadAsZip/';
                            // DownloadAsZip?mimeType=' + data;
                        },
                        error: function (request, status, error) {
                            alert("Error Generating Files");
                            //+ request.responseText);
                        }
                    }).done(function () {
                        $.ajax({
                            type: "POST",
                            url: "/Service/UnZipDownload",
                            data: {},
                            contentType: "application/json; charset=utf-8",
                            success: function (response) {
                                //alert("success in unzip");
                                CallEmail();
                            }
                        })
                    }).;
                }
    

    This i currently am using creates the reports and unzips to new folder but not sure how to chain another even to start the email process.

    if (isValid) { /* At least 1 record is selected */
                    phaseOne = $.ajax({
                        type: "GET",
                        url: "/Service/ExportFiles/",
                        contentType: "application/json; charset=utf-8",
                        traditional: true,
                        data: { "quoteIDs": arrSelectedChks },
    
                        //success: function (response) {
                        //    window.location = '/Service/DownloadAsZip';
                        //    isZipped = true;
                        //}
                    }).then(function () {
                        window.location = '/Service/DownloadAsZip';
                        isZipped = true;
                    });
                }
     if (isValid) {
                    var request = $.ajax({
                        type: "GET",
                        url: "/Service/ExportFiles/",
                        contentType: "application/json; charset=utf-8",
                        traditional: true,
                        data: { "quoteIDs": arrSelectedChks },
                        success: function () {
                            window.location = '/Service/DownloadAsZip';
                            // DownloadAsZip?mimeType=' + data;
                            bool = true;
                        },
                        //error: function (request, status, error) {
                        //    alert("Error Generating Files");
                        //    //+ request.responseText);
                        //}
                    });
                  }
                var phaseTwo = $.when(request);
                phaseTwo.done(UnZipFunc());
    
    /*Need to start 1 more controller action for creating an email */
               });

    /Tried something like this but was unsuccessful

                /* Below i can Create reports / zip / unzip to location and create an email */

                if (isValid) {
                    $.ajax({
                        type: "GET",
                        url: "/Service/ExportFiles/",
                        contentType: "application/json; charset=utf-8",
                        traditional: true,
                        data: { "quoteIDs" : arrSelectedChks },
                        success: function () {
                            window.location = '/Service/DownloadAsZip/';
                            // DownloadAsZip?mimeType=' + data;
                        },
                        error: function (request, status, error) {
                            alert("Error Generating Files");
                            //+ request.responseText);
                        }
                    }).done(function () {
                        $.ajax({
                            type: "POST",
                            url: "/Service/UnZipDownload",
                            data: {},
                            contentType: "application/json; charset=utf-8",
                            success: function (response) {
                                //alert("success in unzip");
                                CallEmail();
                            }
                        })
                    }).;
                }

    // Also tried creating separate methods for each ajax with $.deferred objects and returning a promise             

    if (isValid) { /* At least 1 record is selected */
                    phaseOne = $.ajax({
                        type: "GET",
                        url: "/Service/ExportFiles/",
                        contentType: "application/json; charset=utf-8",
                        traditional: true,
                        data: { "quoteIDs": arrSelectedChks },
    
                        //success: function (response) {
                        //    window.location = '/Service/DownloadAsZip';
                        //    isZipped = true;
                        //}
                    }).then(function () {
                        window.location = '/Service/DownloadAsZip';
                        isZipped = true;
                    });
                }            $.when(phaseOne).then(function () {
                phaseOne.then(function () {
                    if (isZipped) { /* Files are Zipped to start this phase */
                        phaseTwo = $.ajax({
                            type: "POST",
                            url: "/Service/UnZipDocument",
                            data: {},
                            contentType: "application/json; charset=utf-8",
                            traditional: true,
                            success: function (response) {
                                debugger;
                                if (response.Status == "Unzipped") {
                                    myJSON = { "Status": "Unzipped", "FilePath": response.FilePath, "FileName": response.FileName, "FileNames": response.FileNames };
                                    bool = true;
                                    alert('unzip sucess : ' + myJSON);
                                }
                            }
                            /*
                            success: function (response) {
                                alert('success unzip ');
                                debugger;
                                if (response.Status == "Exist") {
                                    window.open("/Service/OpenPDFbrowser?fileName=" + response.FileName + "&path=" + response.FilePath, "_blank");
                                }
                            },*/
                        }).done(function (response) {
                            if (response.Status == "Unzipped") {
                                myJSON = { "Status": "Unzipped", "FilePath": response.FilePath, "FileName": response.FileName, "FileNames": response.FileNames };
                                bool = true;
                            }
                        });
                    }
                });
    
                $.when(phaseTwo).then(function () {
                    if (bool) {
                        $.ajax({
                            type: "GET",
                            url: 
                        })
                        alert('phase 2 WHEN.THEN() ');
                        CallEmail();
                    }
                });
    

    Monday, November 18, 2019 9:48 PM

All replies

  • User-474980206 posted

    There is no event for window.location completion. But why does the client need get involved. The download server code could just do the email itself. to do what you want, you need to start polling the server after setting location, and have it return a status when done. You could store this status in a database. You must be sure the status check does not use session.

    Tuesday, November 19, 2019 12:49 AM
  • User665608656 posted

    Hi ExceedingLife,

    // Also tried creating separate methods for each ajax with $.deferred objects and returning a promise             

    Can you give us the details of the error and the statement in which the error occurred?

    In fact, we don't recommend that you use so much ajax to achieve your requirements. You can do it in the controller, which will be clearer and won't cause many strange issues.

    Best Regards,

    YongQing.

    Tuesday, November 19, 2019 9:21 AM
  • User-544325736 posted

    When I was executing my code I wanted them to go in a certain order (Create Reports, Put them in a .zip. unzip to a certain folder) then email.
    I was getting create report. and when I was in debug mode it was going in this proper order. When I was just executing it normally. the program would try to unzip the file right after creating the report. (there is no file to unzip at that stage) so the program would run then create the zip but because it tried to unzip already the folder would stay zipped.

    I was trying to have it in the success: of ajax. I tried a .done promise after ajax. I tried a bool if statement I tried many different conditions. It seems to finally be working in the correct order when i first open the project and select the records and the button click. Only 1 time it will do it successful. when I try to select new records the unzip folder isnt always created the second time. Or if till be created but empty. 

    Here is what I currently have which sometime works. Once I have this going good then I have to create the last step which will be to create an email and attach the document.

    $('#btnGetChkEmail').on('click', function() {
                var arrChkBoxes = [];
                var arrSelectedChks = [];
                var myJSON = {};
                var phaseOne, phaseTwo;
                var isValid = false;
                var bool = false;
                var isZipped = false;
                var successful = false;
    
                // Turn all selected checkbox T/F values into QuoteIDs
                $("input:checked").each(function (index, value) {
                    arrChkBoxes.push($(value).val());
                });
     if (isValid) { /* At least 1 record is selected */
                    var phaseOne = $.ajax({
                        type: "GET",
                        async: false,
                        url: "/Service/ExportFiles/",
                        contentType: "application/json; charset=utf-8",
                        traditional: true,
                        data: { "quoteIDs": arrSelectedChks },
    
                        success: function (response) {
                            window.location = '/Service/DownloadAsZip';
                            successful = true
                        },
                        complete: function () {
                            if (successful)
                                isZipped = true;
                        }
                    });
                }
    
                $.when(phaseOne).always(function () {
                    if (isZipped) { /* Files are Zipped to start this phase */
                        $.ajax({
                            type: "POST",
                            async: false,
                            url: "/Service/UnZipDocument",
                            data: {},
                            contentType: "application/json; charset=utf-8",
                            traditional: true,
    
                        });
                    }
                })
    });
    
     [HttpPost]
            public JsonResult UnZipDocument()
            {
                if(TempData["ZipName"] != null)
                {
                    bool isValid = false;
                    string fileName = "";
                    string zipName = TempData["ZipName"] as string;
                    string downloadPath = new KnownFolder(KnownFolderType.Downloads).Path;
                    string filePath = new KnownFolder(KnownFolderType.Downloads).Path;
                    downloadPath = Path.GetFullPath(downloadPath);
                    //Make sure last char on path doesn't allow malicious code outside of it.
                    if (!downloadPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
                        downloadPath += Path.DirectorySeparatorChar;
    
                    string zipFolder = zipName.Substring(0, zipName.Length - 4);
                    Directory.CreateDirectory(Path.Combine(downloadPath, zipFolder));
                    TempData["FolderName"] = zipFolder;
                    try
                    {
                        using(ZipArchive archive = ZipFile.OpenRead(downloadPath + zipName))
                        {
                            downloadPath += zipFolder;
    
                            foreach(ZipArchiveEntry entry in archive.Entries)
                            {
                                if(archive.Entries.Count == 1)
                                {
                                    fileName = entry.FullName;
                                }
                                if(entry.FullName.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
                                {
                                    string destinationPath = Path.GetFullPath(Path.Combine(downloadPath, entry.FullName));
                                    entry.ExtractToFile(destinationPath);
                                }
                            }
                            // Unzipped all files into new folder completed
                            isValid = true;
                        }
                    }
                    catch(System.Exception e) { }
    
                    if (isValid)
                    {
                        var folderPath = downloadPath; //Path.Combine(downloadPath);
                        var fileCount = new List<string>();
                        if (Directory.Exists(folderPath))
                        {
                            fileCount = Directory.GetFiles(@folderPath, "*.pdf").ToList();
                        }
    
                        ServiceResponse parameter = new ServiceResponse
                        {
                            Status = "Unzipped",
                            FilePath = folderPath,
                            FileName = fileCount.FirstOrDefault(),
                            FileNames = fileCount
                        };
    
                        return Json(parameter, JsonRequestBehavior.AllowGet);
                    }
                }
                return Json("ZipFail", JsonRequestBehavior.AllowGet);
            }

    Tuesday, November 19, 2019 2:52 PM
  • User475983607 posted

    IMHO, the code is far to complex for such a simple task.  All of the JavaScript logic can be moved to a single Action where each step is a method.  What is the reasoning for the over engineering?

    Anyway, the bug is...

    window.location = '/Service/DownloadAsZip/';

    ... as explained above.

    The browser does not notify JavaScript application when the file is downloaded.  The browser simply downloads the file as requested.

    You'll need to rethink the design.

    Tuesday, November 19, 2019 3:07 PM
  • User-544325736 posted

    honestly im not sure how i should be doing this and setting it up...

    Just been trial and error.

    both my me ways so far can create the report put them in a zip unzip them to a folder but it doesnt work 100% of the time. I have it working most of the time it works around 75% id say and it can even generate a blank email. How would you do a task like this? Here are my two methods that can once in awhile get it done. Sometimes it goes out of order the unzip happens before the zip thats why it doesnt work 100% of the time.

    if (isValid) {
                    $.ajax({
                        type: "GET",
                        url: "/Service/ExportFiles/",
                        contentType: "application/json; charset=utf-8",
                        traditional: true,
                        data: { "quoteIDs" : arrSelectedChks },
                        success: function () {
                            window.location = '/Service/DownloadAsZip/';
                            // DownloadAsZip?mimeType=' + data;
                        },
                        error: function (request, status, error) {
                            alert("Error Generating Files");
                            //+ request.responseText);
                        }
                    }).done(function () {
                        $.ajax({
                            type: "POST",
                            url: "/Service/UnZipDownload",
                            data: {},
                            contentType: "application/json; charset=utf-8",
                            success: function (response) {
                                //alert("success in unzip");
                                CallEmail();
                            }
                        })
                    });

    and

     if (isValid) { /* At least 1 record is selected */
                    var phaseOne = $.ajax({
                        type: "GET",
                        async: false,
                        url: "/Service/ExportFiles/",
                        contentType: "application/json; charset=utf-8",
                        traditional: true,
                        data: { "quoteIDs": arrSelectedChks },
    
                        success: function (response) {
                            window.location = '/Service/DownloadAsZip';
                            successful = true
                        },
                        complete: function () {
                            if (successful)
                                isZipped = true;
                        }
                    });
                }
    
                $.when(phaseOne).always(function () {
                    if (isZipped) { /* Files are Zipped to start this phase */
                        $.ajax({
                            type: "POST",
                            async: false,
                            url: "/Service/UnZipDocument",
                            data: {},
                            contentType: "application/json; charset=utf-8",
                            traditional: true,
    
                        });
                    }
                })

    Tuesday, November 19, 2019 4:29 PM
  • User-474980206 posted

    as the ziping, unziping and mailing is all done on the server, why involve the client in the logic?

    public ActionResult (string[] quoteids)
    {
        var folderName = createReports(quoteids);
        var fileName = createZip(folderName);
        email(fileName);
        return File(fileName, "application/zip");
    }

    Tuesday, November 19, 2019 6:10 PM
  • User-544325736 posted

    with MVC the only way to be able to do these steps with the process of 1 button this was the only way i figured how how. I could create 1 report without using ajax but if i wanted to create multiple i needed to use ajax. and the only way to do multiple is to put them all in a zip and return the 1 zip file. mvc only allows the return of 1 file. then i unzipped it to have it easier to be able to use in an email.

    Tuesday, November 19, 2019 6:22 PM
  • User-474980206 posted

    while you return a zip file to the client (which the user may never extract), all the server code is using files generated on the server, individual and zip. the server can do as many steps as it wants. your current server logic is

    request 1 => produce report files -> return ok
    request 2 => return report files produced in step 1 as zip 
    request 3 => email report files produced in step 1 -> return ok

    in all cases the server needs to track the the report files, unless the client is supposed to re-upload the zip file for request 3. this can easily be done in 1 server request.

    Tuesday, November 19, 2019 11:13 PM
  • User-544325736 posted

    So I have tried SO MANY DIFFERENT results to try to get this to work with the click of 1 button. BUT the only way I have found out a way to get this to work is to have 1 button to do the report creating and zipping of the files. Then to have a second button (mines currently in a modal) and on this button click I am unzipping the file and creating the email. I have not been able to do everything all in 1 call. When I try my unzip functions is being called before my zip function I have tried many different ways to try to not have this happen [success, .done(), deferred objects, javascript promises]

    $('#btnGetChkEmail').on('click', function() {
                var arrChkBoxes = [];
                var arrSelectedChks = [];
                var myJSON = {};
                var phaseOne, phaseTwo;
                var isValid = false;
                var bool = false;
                var isZipped = false;
                //var unZipped = false;
                var successful = false;
    
                // Turn all selected checkbox T/F values into QuoteIDs
                $("input:checked").each(function (index, value) {
                    arrChkBoxes.push($(value).val());
                });
                // Push all Selected QIDs on NEW ARRAY
                $.each(arrChkBoxes, function (key, value) {
                    if (IsPositiveInteger(value)) {
                        arrSelectedChks.push(value);
                    }
                });
    
                if (arrSelectedChks.length === 0) { // Create Modal (Error ~ None ~ Selected)
                    isValid = false;
                    alert("No Records Selected");
                } else {
                    isValid = true;
                }
    
            /* EDITION LIKE GETCHKS */
                if (isValid) {
                    $.ajax({
                        type: "GET",
                        url: "/Service/ExportFiles/",
                        contentType: "application/json; charset=utf-8",
                        traditional: true,
                        data: { "quoteIDs": arrSelectedChks },
                        success: function (response) {
                            window.location = '/Service/DownloadAsZip';
                        },
                        error: function (request, status, error) {
                            alert("Error Generating Reports");
                        }
                    }).done(function (data) {
                        /* Only way to do this 100% at the moment is to bring up a Button in a Modal */
                        var zipModal = $("#zipModEmail");
                        var modalHead = "<h3 class='modal-title'>Generate Email Messages</h3>";
                        var modalBody = "<span class='glyphicon glyphicon-ok-sign' style='font-size:5em; color:green;'></span>" +
                            "<p><b>Attach PDF's to email Confirmation</b></p>" +
                            "<p>Click 'OK' to Confirm</p>";
                        //var modalFoot = "";
                        zipModal.find(".modal-header").html(modalHead);
                        zipModal.find(".modal-header").addClass("alert-success");
                        zipModal.find(".modal-body").html(modalBody);
                        zipModal.modal("show");
                    });
                }
    $("#btnUnNemail").click(function (e) {
                var myJSON = {};
                var bool = false;
    
                var ajaxCall = $.ajax({
                                type: "POST",
                                url: "/Service/UnZipDocument",
                                data: {},
                                contentType: "application/json; charset=utf-8",
                                success: function (response) {
                                    debugger;
                                    if (response.Status == "Unzipped") {
                                        myJSON = { "Status": "Unzipped", "FilePath": response.FilePath, "FileName": response.FileName, "FileNames": response.FileNames };
                                        bool = true;
                                    }
                                    $("#zipModEmail").modal("hide");
    
                                }
                            });
    
                // Try switching to 'POST'
                $.when(ajaxCall).then(function () {
                    if (bool) {
                        //debugger;
                        $.ajax({
                            type: "GET",
                            url: '@Url.Action("CreateEmailReport", "Service")',
                            contentType: "application/json; charset=utf-8;",
                            data: { "folderData": myJSON },
                            traditional: true,
                        })
                    }
                });
                
    
            });
    

    Wednesday, November 20, 2019 5:31 PM
  • User-474980206 posted

    as there is no way for the client (javascript) to know when the second step (zip files) is done, the rest is pure fallacy.

    if you, for whatever reason, insist on client orchestration of the steps, you need to move the download of the zip to last, as it can not be a dependency for any following steps. 

    Wednesday, November 20, 2019 5:57 PM
  • User-544325736 posted

    So you are suggesting I rebuild the controller action. in 1 action create the report. put it in a zip file and unzip it to a specified folder and return the folder to the browser? I could try that. and also opening and creating an email with the attachment I will see if i can get this working all in 1 action

    Wednesday, November 20, 2019 7:38 PM
  • User475983607 posted

    ExceedingLife

    So you are suggesting I rebuild the controller action. in 1 action create the report. put it in a zip file and unzip it to a specified folder and return the folder to the browser? I could try that. and also opening and creating an email with the attachment I will see if i can get this working all in 1 action

    Why do you zip then unzip the files?  I would save the files, send an email with attachments, zip the files, return the zipped file, clean up the files.

    Is there something you're not telling us.  We've discussed that it is not possible to download multiple files and this includes folders.  By chance are you extracting the files to a directory on your development machine?  If so, this will not work when deployed to a remote web server as the files will exist on the web server.

    Wednesday, November 20, 2019 8:08 PM
  • User-544325736 posted

    I need the program to create multiple reports. all depends what checkboxes are selected. the only way I have been able to figure out to do this is to create a zip file and put all the reports in there. The reason why i unzip it after is so the user doesnt have to unzip them they will be able to just go to the folder and open and review the report. 

    I have not been able to save the files without using a zip. I can save 1 report yes but i cannot save multiples unless its in a zip. if everything is good i have another button that will create an email and automatically attach the report to the email and the user can review the email it adds the email address that is in the report to the email. if it looks good they can click send and good. in the future this is planned to be all automatic but for now we can to make sure everything is working properly and looks good b4 they send the emails

    Friday, November 22, 2019 6:11 PM
  • User475983607 posted

    ExceedingLife

    I need the program to create multiple reports. all depends what checkboxes are selected. the only way I have been able to figure out to do this is to create a zip file and put all the reports in there. The reason why i unzip it after is so the user doesnt have to unzip them they will be able to just go to the folder and open and review the report.

    What folder?  The user has access to the web server?

    Friday, November 22, 2019 6:18 PM
  • User-474980206 posted

    your logic makes no sense.

    if the report folder is file share on the server the user can access, why the zip file copy?
    did you think unziping the file on the server magically unzipped it on the client?

    Friday, November 22, 2019 9:17 PM
  • User-544325736 posted

    I am using 

    using System.IO;
    using System.IO.Compression;
    using Syroot.Windows.IO;
    using System.Diagnostics;
    
    string downloadPath = new KnownFolder(KnownFolderType.Downloads).Path;
    

    To get the current users Download folder.

    currently my program is creating each report putting them all in a zip. I want the zip to be in the users download folder on their PC. I havent tested it yet when deploying it.

    Monday, November 25, 2019 3:50 PM
  • User475983607 posted

    currently my program is creating each report putting them all in a zip. I want the zip to be in the users download folder on their PC. I havent tested it yet when deploying it.

    I knew there was something missing from your explanation.  The design will not work.  The C# code runs on the web server not the client's machine.  It seems to work while developing because the development machine is both the client and the server.

    Monday, November 25, 2019 3:54 PM
  • User-474980206 posted

    also when running in a server (unlike running in VS), there is no user profile for services, so known folders will be null.

    Monday, November 25, 2019 6:48 PM
  • User-544325736 posted

    I may have some more questions about this coming in the next day or 2.

    I am going to mess around with this to see if i can figure out a correct way or another way to get this to work, I would like to download the reports to the users machine. 

    Monday, November 25, 2019 8:15 PM
  • User475983607 posted

    I would look into updating the actual report to take a input and produce the report(s) rather than executing many reports.  

    ExceedingLife

    I would like to download the reports to the users machine. 

    Remember you get one file download.  Multiple separate reports must be zipped then downloaded.  Otherwise, find a way to create one report that contains multiple sub-reports.  

    Monday, November 25, 2019 8:26 PM