Download bestand van een ASP.NET Web API-methode met AngularJS

In mijn Angular JS-project heb ik een <a>-ankertag, die wanneer erop wordt geklikt een HTTP GET-verzoek doet aan een WebAPI-methode die een bestand retourneert .

Nu wil ik dat het bestand naar de gebruiker wordt gedownload zodra het verzoek is gelukt. Hoe doe ik dat?

De ankertag:

<a href="#" ng-click="getthefile()">Download img</a>

AngularJS:

$scope.getthefile = function () {        
    $http({
        method: 'GET',
        cache: false,
        url: $scope.appPath + 'CourseRegConfirm/getfile',            
        headers: {
            'Content-Type': 'application/json; charset=utf-8'
        }
    }).success(function (data, status) {
        console.log(data); // Displays text data if the file is a text file, binary if it's an image            
        // What should I write here to download the file I receive from the WebAPI method?
    }).error(function (data, status) {
        // ...
    });
}

Mijn WebAPI-methode:

[Authorize]
[Route("getfile")]
public HttpResponseMessage GetTestFile()
{
    HttpResponseMessage result = null;
    var localFilePath = HttpContext.Current.Server.MapPath("~/timetable.jpg");
    if (!File.Exists(localFilePath))
    {
        result = Request.CreateResponse(HttpStatusCode.Gone);
    }
    else
    {
        // Serve the file to the client
        result = Request.CreateResponse(HttpStatusCode.OK);
        result.Content = new StreamContent(new FileStream(localFilePath, FileMode.Open, FileAccess.Read));
        result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
        result.Content.Headers.ContentDisposition.FileName = "SampleImg";                
    }
    return result;
}

Antwoord 1, autoriteit 100%

Ondersteuning voor het downloaden van binaire bestanden in het gebruik van ajax is niet geweldig, het is nog steeds in ontwikkeling als werkende concepten.

Eenvoudige downloadmethode:

Je kunt de browser het gevraagde bestand laten downloaden door simpelweg de onderstaande code te gebruiken, en dit wordt ondersteund in alle browsers, en zal uiteraard het WebApi-verzoek op dezelfde manier activeren.

$scope.downloadFile = function(downloadPath) { 
    window.open(downloadPath, '_blank', '');  
}

Ajax binaire downloadmethode:

Ajax gebruiken om het binaire bestand te downloaden kan in sommige browsers en hieronder is een implementatie die zal werken in de nieuwste versies van Chrome, Internet Explorer, FireFox en Safari.

Het gebruikt een arraybuffer-antwoordtype, dat vervolgens wordt omgezet in een JavaScript blob, die vervolgens wordt gepresenteerd om op te slaan met behulp van de saveBlobmethode – hoewel dit momenteel alleen aanwezig is in Internet Explorer – of omgezet in een blob-gegevens-URL die wordt geopend door de browser, waardoor het downloaddialoogvenster wordt geactiveerd als het mime-type wordt ondersteund voor weergave in de browser.

Internet Explorer 11-ondersteuning (vast)

Opmerking: Internet Explorer 11 hield er niet van om de functie msSaveBlobte gebruiken als deze onder een alias was gezet – misschien een beveiligingsfunctie, maar waarschijnlijker een fout, dus het gebruik van var saveBlob = navigator.msSaveBlob || navigator.webkitSaveBlob ... etc.om de beschikbare saveBlob-ondersteuning te bepalen, veroorzaakte een uitzondering; daarom test de onderstaande code nu afzonderlijk voor navigator.msSaveBlob. Bedankt? Microsoft

// Based on an implementation here: web.student.tuwien.ac.at/~e0427417/jsdownload.html
$scope.downloadFile = function(httpPath) {
    // Use an arraybuffer
    $http.get(httpPath, { responseType: 'arraybuffer' })
    .success( function(data, status, headers) {
        var octetStreamMime = 'application/octet-stream';
        var success = false;
        // Get the headers
        headers = headers();
        // Get the filename from the x-filename header or default to "download.bin"
        var filename = headers['x-filename'] || 'download.bin';
        // Determine the content type from the header or default to "application/octet-stream"
        var contentType = headers['content-type'] || octetStreamMime;
        try
        {
            // Try using msSaveBlob if supported
            console.log("Trying saveBlob method ...");
            var blob = new Blob([data], { type: contentType });
            if(navigator.msSaveBlob)
                navigator.msSaveBlob(blob, filename);
            else {
                // Try using other saveBlob implementations, if available
                var saveBlob = navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
                if(saveBlob === undefined) throw "Not supported";
                saveBlob(blob, filename);
            }
            console.log("saveBlob succeeded");
            success = true;
        } catch(ex)
        {
            console.log("saveBlob method failed with the following exception:");
            console.log(ex);
        }
        if(!success)
        {
            // Get the blob url creator
            var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
            if(urlCreator)
            {
                // Try to use a download link
                var link = document.createElement('a');
                if('download' in link)
                {
                    // Try to simulate a click
                    try
                    {
                        // Prepare a blob URL
                        console.log("Trying download link method with simulated click ...");
                        var blob = new Blob([data], { type: contentType });
                        var url = urlCreator.createObjectURL(blob);
                        link.setAttribute('href', url);
                        // Set the download attribute (Supported in Chrome 14+ / Firefox 20+)
                        link.setAttribute("download", filename);
                        // Simulate clicking the download link
                        var event = document.createEvent('MouseEvents');
                        event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
                        link.dispatchEvent(event);
                        console.log("Download link method with simulated click succeeded");
                        success = true;
                    } catch(ex) {
                        console.log("Download link method with simulated click failed with the following exception:");
                        console.log(ex);
                    }
                }
                if(!success)
                {
                    // Fallback to window.location method
                    try
                    {
                        // Prepare a blob URL
                        // Use application/octet-stream when using window.location to force download
                        console.log("Trying download link method with window.location ...");
                        var blob = new Blob([data], { type: octetStreamMime });
                        var url = urlCreator.createObjectURL(blob);
                        window.location = url;
                        console.log("Download link method with window.location succeeded");
                        success = true;
                    } catch(ex) {
                        console.log("Download link method with window.location failed with the following exception:");
                        console.log(ex);
                    }
                }
            }
        }
        if(!success)
        {
            // Fallback to window.open method
            console.log("No methods worked for saving the arraybuffer, using last resort window.open");
            window.open(httpPath, '_blank', '');
        }
    })
    .error(function(data, status) {
        console.log("Request failed with status: " + status);
        // Optionally write the error out to scope
        $scope.errorDetails = "Request failed with status: " + status;
    });
};

Gebruik:

var downloadPath = "/files/instructions.pdf";
$scope.downloadFile(downloadPath);

Opmerkingen:

U moet uw WebApi-methode aanpassen om de volgende koppen te retourneren:

  • Ik heb de x-filenameheader gebruikt om de bestandsnaam te verzenden. Dit is voor het gemak een aangepaste header, maar u kunt de bestandsnaam uit de content-disposition-header halen met behulp van reguliere expressies.

  • U moet ook de content-typemime-header voor uw reactie instellen, zodat de browser het gegevensformaat kent.

Ik hoop dat dit helpt.


Antwoord 2, autoriteit 4%

C# WebApi PDF-download die allemaal werkt met Angular JS-authenticatie

Web API-controller

[HttpGet]
    [Authorize]
    [Route("OpenFile/{QRFileId}")]
    public HttpResponseMessage OpenFile(int QRFileId)
    {
        QRFileRepository _repo = new QRFileRepository();
        var QRFile = _repo.GetQRFileById(QRFileId);
        if (QRFile == null)
            return new HttpResponseMessage(HttpStatusCode.BadRequest);
        string path = ConfigurationManager.AppSettings["QRFolder"] + + QRFile.QRId + @"\" + QRFile.FileName;
        if (!File.Exists(path))
            return new HttpResponseMessage(HttpStatusCode.BadRequest);
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
        //response.Content = new StreamContent(new FileStream(localFilePath, FileMode.Open, FileAccess.Read));
        Byte[] bytes = File.ReadAllBytes(path);
        //String file = Convert.ToBase64String(bytes);
        response.Content = new ByteArrayContent(bytes);
        response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
        response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
        response.Content.Headers.ContentDisposition.FileName = QRFile.FileName;
        return response;
    }

Angular JS-service

this.getPDF = function (apiUrl) {
            var headers = {};
            headers.Authorization = 'Bearer ' + sessionStorage.tokenKey;
            var deferred = $q.defer();
            $http.get(
                hostApiUrl + apiUrl,
                {
                    responseType: 'arraybuffer',
                    headers: headers
                })
            .success(function (result, status, headers) {
                deferred.resolve(result);;
            })
             .error(function (data, status) {
                 console.log("Request failed with status: " + status);
             });
            return deferred.promise;
        }
        this.getPDF2 = function (apiUrl) {
            var promise = $http({
                method: 'GET',
                url: hostApiUrl + apiUrl,
                headers: { 'Authorization': 'Bearer ' + sessionStorage.tokenKey },
                responseType: 'arraybuffer'
            });
            promise.success(function (data) {
                return data;
            }).error(function (data, status) {
                console.log("Request failed with status: " + status);
            });
            return promise;
        }

Een van beide is voldoende

Angular JS-controller die de service aanroept

vm.open3 = function () {
        var downloadedData = crudService.getPDF('ClientQRDetails/openfile/29');
        downloadedData.then(function (result) {
            var file = new Blob([result], { type: 'application/pdf;base64' });
            var fileURL = window.URL.createObjectURL(file);
            var seconds = new Date().getTime() / 1000;
            var fileName = "cert" + parseInt(seconds) + ".pdf";
            var a = document.createElement("a");
            document.body.appendChild(a);
            a.style = "display: none";
            a.href = fileURL;
            a.download = fileName;
            a.click();
        });
    };

En als laatste de HTML-pagina

<a class="btn btn-primary" ng-click="vm.open3()">FILE Http with crud service (3 getPDF)</a>

Dit wordt aangepast door de code te delen, nu hoop ik dat iemand er iets aan heeft, want het heeft even geduurd voordat ik dit werkend kreeg.


Antwoord 3, autoriteit 2%

Voor mij was de Web API Rails en client-side Angular gebruikt met Restangularen FileSaver.js

Web-API

module Api
  module V1
    class DownloadsController < BaseController
      def show
        @download = Download.find(params[:id])
        send_data @download.blob_data
      end
    end
  end
end

HTML

<a ng-click="download('foo')">download presentation</a>

Hoekcontroller

$scope.download = function(type) {
    return Download.get(type);
  };

Angulaire service

'use strict';
app.service('Download', function Download(Restangular) {
  this.get = function(id) {
    return Restangular.one('api/v1/downloads', id).withHttpConfig({responseType: 'arraybuffer'}).get().then(function(data){
      console.log(data)
      var blob = new Blob([data], {
        type: "application/pdf"
      });
      //saveAs provided by FileSaver.js
      saveAs(blob, id + '.pdf');
    })
  }
});

Other episodes