Download Multiple Files Without Using Zip File

2019-01-10 23:52发布

问题:

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.

回答1:

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

Inspired by this SO post

jQuery Plugins:

  • json2.js
  • $.cookie()
  • $.bbq()

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);
});


回答2:

jQuery plugin which will do the work for you:

github.com/biesiad/multiDownload



回答3:

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>");


回答4:

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.



回答5:

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>