Download Multiple Files Without Using Zip File
Asked Answered
P

5

12

I have a generic handler Document.ashx that creates Word documents on the fly by reading information from the querystring like this Document.ashx?clientid=123&documentid=10 and it works perfectly.

I need to create an interface with a list of checkboxes and a Download All button. The best idea I've had so far is to use something like this to make the calls to the handler.

$("body").append("<iframe src='Document.ashx?clientid=123&documentid=10'></iframe>
                  <iframe src='Document.ashx?clientid=123&documentid=11'></iframe>")

Chrome and Firefox handle this as expected, though IE9 prompts the user to ask if they want to save the first file but ignores the following files.

How do I initiate downloading of multiple files from the client?

This is for an intranet site so the files are always generated in ~1 second, users would be selecting ~3-5 documents at a time. The vast majority of users are using IE9. I can tell everyone they have to use Firefox or Chrome but I'd rather find a solution that works in all modern browsers.

I don't want to create a zip file server side because then they always have to unzip it first (which will be too difficult for some to understand) and it slows them down.

Provisory answered 28/1, 2012 at 18:20 Comment(1)
I'm gonna try something like thisProvisory
P
11

So this is propably overkill but works in IE9, FF7, and Chrome 16:

Inspired by this SO post

jQuery Plugins:

C# in handler:

public void ProcessRequest (HttpContext context) {

    ...

    if (!string.IsNullOrEmpty(context.Request.QueryString["downloadid"])) 
          Response.Cookies[context.Request.QueryString["downloadid"]].Value = "complete";
}

Javascript/jQuery:

function downloadFile(url, downloadid) {
    //set a cookie with a unique download id
    $.cookie(downloadid, 'pending', { path: '/' });

    //create a new url
    var newurl = $.param.querystring(url, { downloadid: downloadid });

    //append an iframe with new url
    $("body").append("<iframe style='height:0;width:0;' data-downloadid='" + downloadid + "' src='" + newurl + "'></iframe>");
}

function downloadComplete(downloadid) {
    //check if download is pending
    return $.cookie(downloadid) == "complete";
}

function downloadManager(arrDownloads) {
    //loop through download items backwards
    var allComplete = false;
    for (var i = arrDownloads.length; i > 0; i--) {
        if (downloadComplete(arrDownloads[i - 1].downloadid)) {
            //download the next one if it exists
            if (i == arrDownloads.length) {
                allComplete = true;
            }
            else {
                downloadFile(arrDownloads[i].url, arrDownloads[i].downloadid);
            }
            //stop checking for completed downloads
            break;
        }
    }

    if (allComplete) {
        //remove cookies
        for (var i = arrDownloads.length; i > 0; i--) {
            $.cookie(arrDownloads[i - 1].downloadid, null, { path: '/' });
        }

        //remove iframes
        $("iframe[data-downloadid]").remove();
    }
    else {
        setTimeout("downloadManager(" + JSON.stringify(arrDownloads) + ");", 500);
    }
}

function downloadFiles(arrurls) {
    var arrDownloads = [];

    for (var i = 0; i < arrurls.length; i++) {
        var item = new Object();
        item.url = arrurls[i];
        item.downloadid = newGuid();
        arrDownloads.push(item);
    }

    //start the first download
    downloadFile(arrDownloads[0].url, arrDownloads[0].downloadid);
    //initiate the manager
    downloadManager(arrDownloads);
}

$(function () {
    var arrurls = [];
    arrurls.push("Document.ashx?clientid=123&documentid=10");
    arrurls.push("Document.ashx?clientid=123&documentid=11");
    arrurls.push("Document.ashx?clientid=123&documentid=12");
    arrurls.push("Document.ashx?clientid=123&documentid=13");
    arrurls.push("Document.ashx?clientid=123&documentid=14");
    downloadFiles(arrurls);
});
Provisory answered 29/1, 2012 at 0:51 Comment(1)
It depends on first call's success. What if it is failed how will it go ahead and call downloadFile for next?Noddy
M
5

jQuery plugin which will do the work for you:

github.com/biesiad/multiDownload

Mikkimiko answered 19/7, 2012 at 6:48 Comment(3)
Chrome still give you a yellow bar saying "This site is attempting to download multiple files. Do you want to allow this?" after downloading the first file. dl.dropbox.com/u/3115379/screengrab_20120719101330.pngProvisory
Well.. you are trying to download multiple files. Yellow bar is right. If you want to avoid it you could add more delay between downloads. Check out 'delay' option.Mikkimiko
i have put images instead of tar.zip in your github test and did not work on chrome latest versionMosora
H
0

This may or may not solve your issue, but you're making a common error here. Iframes are not self-closing tags. Many browsers will not correctly parse this html. Try making it

$("body").append("<iframe src='Document.ashx?clientid=123&documentid=10'>If you can read this, please use a newer browser</iframe>
                  <iframe src='Document.ashx?clientid=123&documentid=11'>If you can read this, please use a newer browser</iframe>")

Also, you way want to try appending each iframe independently, as browsers may not be recognizing the frames correctly when added all at once:

$("body").append("<iframe src='Document.ashx?clientid=123&documentid=10'>If you can read this, please use a newer browser</iframe>");
$("body").append("<iframe src='Document.ashx?clientid=123&documentid=10'>If you can read this, please use a newer browser</iframe>");
Holm answered 28/1, 2012 at 19:8 Comment(1)
Using 2 $.append() statements worked better (Chrome didn't throw up a warning about downloading multiple files) but it still doesn't work in IE9 (it only recognizes the first file)Provisory
B
0

I know this is an old question, but I have had this issue recently and created this solution which best suited my needs (I did not want to use any cookies and wanted the code to be as simple as possible). In code behind:

Protected Sub DownloadFile(fileId As String)
    If Request.Browser.Browser = "Chrome" Then
        'open iframes dynamically for multiple downloads
        ClientScript.RegisterStartupScript(Me.GetType(), fileId, _
                                           "<script language='javascript'>createIframeForDownloadHandler('" & fileId & "');</script>")
    Else
        'open windows for multiple downloads
        ClientScript.RegisterStartupScript(Me.GetType(), fileId, _
                                           "<script language='javascript'>openWindowForDownloadHandler('" & fileId & "');</script>")
    End If
End Sub

Here are the javascript functions:

function openWindowForDownloadHandler(fileId) {
    //open a new window. setting height and width foces new window as opposed to new tab
    window.open('FileShareDownloadHandler.ashx?id=' + fileId, '_blank', 'width=100,height=100,left=0,top=0');
}

function createIframeForDownloadHandler(fileId) {
    var element = document.createElement("iframe");
    element.setAttribute('id', 'myframe' + fileId);
    element.setAttribute('style', 'display:none;');
    element.setAttribute('src', 'FileShareDownloadHandler.ashx?id=' + fileId);
    document.body.appendChild(element);
}

Putting DownloadFile(id) inside a loop works well.

Basically, I found chrome works well with handling the iFrames, but IE doesn't (I am using IE9). This has been working for me on Crome v26, FF v19, IE9.

Bedplate answered 25/4, 2013 at 15:24 Comment(0)
T
0

You can call to download in a javascript loop like this:

<a id="downloadAll" href="#">Download All</a>

<script>
var downloadSelected = $('a#downloadSelected');
var doc_ids = ['10', '11'];
for (var i in doc_ids) {
    var uri = 'Document.ashx?clientid=123&documentid=' + doc_ids[i];
    var iframe = $("<iframe/>").attr({
                     src: uri,
                     style: "visibility:hidden;display:none"
                 }).appendTo(downloadAll);
}
</script>
Talamantes answered 12/12, 2016 at 5:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.