POST Ajax and redirect file response to the browser

Yesterday a collegue of mine had a problem:
How post an ajax form and redirect the file result to the browser.
If you post a form to an handler that response with a file the result will be downloaded by your browser.
But how can we achive this behaviour via ajax?
We looked for a solution and we found something but not completed.
My collegue worked on this and here his result.
It’s written in javascript and not using jquery because jquery has some problems managing bynary response

function getFileFromHTTPRequest(modelForm, fileGenAction) {
    var xhr = new XMLHttpRequest();
    xhr.open('POST', fileGenAction, true);
    xhr.responseType = 'blob';
    xhr.onload = function () {
        if (this.status === 200) {
            var filename = "";
            var disposition = xhr.getResponseHeader('Content-Disposition');
            if (disposition && disposition.indexOf('attachment') !== -1) {
                var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                var matches = filenameRegex.exec(disposition);
                if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
            }
            var type = xhr.getResponseHeader('Content-Type');

            var blob = new Blob([this.response], { type: type });
            if (typeof window.navigator.msSaveBlob !== 'undefined') {
                // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
                window.navigator.msSaveBlob(blob, filename);
            } else {
                var URL = window.URL || window.webkitURL;
                var downloadUrl = URL.createObjectURL(blob);

                if (filename) {
                    // use HTML5 a[download] attribute to specify filename
                    var a = document.createElement("a");
                    // safari doesn't support this yet
                    if (typeof a.download === 'undefined') {
                        window.location = downloadUrl;
                    } else {
                        a.href = downloadUrl;
                        a.download = filename;
                        document.body.appendChild(a);
                        a.click();
                    }
                } else {
                    window.location = downloadUrl;
                }

                setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
            }
        } else if (this.status === 400)
            alert("The query returns too many elements, please select some filters.");
        else
            alert("Can’t download the file. Error code: " + this.status);
    };
    xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    xhr.send(modelForm.serialize());
}

How to use it by javascript

function getScreenElements() {
    getFileFromHTTPRequest($(".form-inline"), '@Url.Action("GetScreenElements")');
}

Here a sample of C# code to handle the request and response an excel file

[HttpPost]
public ActionResult GetScreenElements(SearchModel vm)
{
    using (var db = new EFContext())
    {
        var count = db.GetElementsCount(vm.Parameter1, vm.Parameter2...);
        if (count > 30000)
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "Bad Request");
        var items = db.GetElements(vm.Parameter1, vm.Parameter2...).ToList();
        using (MemoryStream ms = new MemoryStream())
        {
            var dtt = ConvertToDataTable(items);
            var wb = new XLWorkbook();
            wb.Worksheets.Add(dtt, "Elements");
            wb.SaveAs(ms);
            return File(ms.ToArray(), System.Net.Mime.MediaTypeNames.Application.Octet, "Element List.xlsx");
        }
    }
}

#region Helper
private DataTable ConvertToDataTable(IList data)
{
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
    DataTable table = new DataTable();
    foreach (PropertyDescriptor prop in properties)
        table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
    foreach (T item in data)
    {
        DataRow row = table.NewRow();
        foreach (PropertyDescriptor prop in properties)
            row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
        table.Rows.Add(row);
    }
    table.AcceptChanges();
    return table;
}
#endregion
Annunci

Informazioni su Andrea Regoli

Project Manager .Net Developer WPF WP7 Asp.Net c# javascript ajax SQL sharepoint
Questa voce è stata pubblicata in .Net, Asp.Net, c#, MVC e contrassegnata con , , . Contrassegna il permalink.

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...