I'm working on an MVC site to allow customers to select multiple documents for download. I wrote the code that allowed them to generate a PDF of any transaction individually. The new requirement is to allow selection of rows and then generate a zip file containing all the PDFs generated.
The web page is setup with a filter section at the top, the transactions in a table after that and below the table are the paging controls. The entire page is wrapped in a form. Since the data I needed was already in the table I split the page into two forms, one for filtering and one for downloading. Problem was the paging controls were part of the form and needed the filtering controls to be submitted along with the page. I moved the paging above the table so it could be part of the filter form while retaining the table form for my multiple PDF download action. This worked great but we have several pages with this setup so to keep the site uniform I would have to move the paging to the top of all those pages. Also, marketing is not crazy about this change but understand if that's what's needed.
Since I can't embed a form inside a form I decided to add attributes to each row's checkbox and use jQuery to go through them all and pull out the data I need for my action. This part works great, I get all the data I need, I put them into an object array, do the json stringify and my controller is happy. I find this question: Download a file by jQuery.Ajax and try some of the answers. After some fiddling with parameters I get it to return binary data. I find a solution to allow my ajax call to download the file it receives and looks like it's working but when I open the zip file I get "Windows cannot open the folder. The Compressed (zipped) folder 'c:\path\deliveries.zip' is invalid".
I know the action worked before because it worked when wired up as a form, I simply had the form action open on a _blank target. I should mention I'm stuck with jQuery 1.11 because this app is big and updating jQuery would require a week or more of testing everything page and functionality.
Here is the controller code (which works fine)
[HttpPost]
public ActionResult GetMultiplePdfs(List<DeliveryPdfDownloadViewModel> deliveries)
{
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach (var delivery in deliveries)
{
string file = $"Delivery-{delivery.CustomerNumber}-{delivery.siteNumber}-{delivery.Time.ToString("yyyyMMddhhmmss")}.pdf";
var url = $"/api/Delivery?customerNumber={delivery.CustNum}&shipToNumber={delivery.ShipToNum}&startTime={delivery.StartTime}";
var response = _client.GetAsync(url).GetAwaiter().GetResult();
var pdf = archive.CreateEntry(file);
using(var entryStream = pdf.Open())
using(var streamWriter = new StreamWriter(entryStream, Encoding.UTF8))
{
var byteArray = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult();
streamWriter.BaseStream.Write(byteArray, 0, byteArray.Length);
streamWriter.Flush();
}
}
}
logger.Info($"Successfully created zip file with {deliveries.Count()} PDF documents in it");
return File(memoryStream.ToArray(), "application/zip", "deliveries.zip");
}
}
Here is the jQuery
$.ajax({
url: "@Url.Action("GetMultiplePdfs")",
type: "POST",
//dataType: 'application/zip',
data: { "deliveries": data },
//xhrFields: {
// responseType: "blob"
//},
success: function (result) {
let bobloblaw = new Blob([result]);
let a = document.createElement('a');
a.href = window.URL.createObjectURL(bobloblaw);
a.download = "deliveries.zip";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(a.href);
},
error: function () {
alert("Unable to download PDFs")
}
});
//subscribeRequest.done(function (data) {
// console.log(data);
//});
I included some commented out lines that I tried. My action wouldn't recognize the json I sent until I got rid of contentType. My ajax call was failing until I removed the dataType; I tried using a subscribeRequest instead of success for that with the same results. I tried setting the responseType in the ajax call to blob. When creating my Blob I tried { type: "application/zip" }, I also tried "application/octetstream" found from the link above.