web-dev-qa-db-fra.com

Téléchargez le fichier à partir d'une méthode API Web ASP.NET à l'aide d'AngularJS

Dans mon projet Angular JS, j'ai une balise d'ancrage <a> qui, lorsqu'elle est cliquée, crée une demande HTTP GET à une méthode WebAPI qui renvoie un fichier. 

Maintenant, je veux que le fichier soit téléchargé sur l'utilisateur une fois que la demande a abouti. Comment je fais ça?

La balise d'ancrage: 

<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) {
        // ...
    });
}

Ma méthode WebAPI:

[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;
}
129
Amar Duplantier

La prise en charge du téléchargement de fichiers binaires à l’aide d’ajax n’est pas excellente, c’est toujours en cours de développement en tant que brouillons de travail

Méthode de téléchargement simple:

Vous pouvez demander au navigateur de télécharger le fichier demandé simplement en utilisant le code ci-dessous. Ceci est pris en charge par tous les navigateurs et déclenchera évidemment la demande WebApi de la même manière.

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

Méthode de téléchargement binaire Ajax:

Utiliser ajax pour télécharger le fichier binaire peut être utilisé dans certains navigateurs. Vous trouverez ci-dessous une implémentation qui fonctionnera dans les dernières versions de Chrome, Internet Explorer, FireFox et Safari.

Il utilise un type de réponse arraybuffer, qui est ensuite converti en JavaScript blob, qui est ensuite présenté pour être sauvegardé à l'aide de la méthode saveBlob - bien que cela ne soit présent que dans Internet Explorer - ou transformé en une URL de données d'objets blob ouverte par le navigateur, déclenchant la boîte de dialogue de téléchargement si le type MIME est pris en charge pour l'affichage dans le navigateur.

Prise en charge d'Internet Explorer 11 (corrigé)

Remarque: Internet Explorer 11 n'aimait pas utiliser la fonction msSaveBlob s'il avait été aliasé - peut-être une fonctionnalité de sécurité, mais plutôt une faille. L'utilisation de var saveBlob = navigator.msSaveBlob || navigator.webkitSaveBlob ... etc. pour déterminer le support disponible pour saveBlob a donc provoqué une exception. c'est pourquoi le code ci-dessous teste maintenant navigator.msSaveBlob séparément. Merci? 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;
    });
};

Usage:

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

Remarques:

Vous devez modifier votre méthode WebApi pour renvoyer les en-têtes suivants:

  • J'ai utilisé l'en-tête x-filename pour envoyer le nom de fichier. Ceci est un en-tête personnalisé pour plus de commodité, vous pouvez toutefois extraire le nom de fichier de l'en-tête content-disposition à l'aide d'expressions régulières.

  • Vous devez également définir l'en-tête content-type mime pour votre réponse afin que le navigateur connaisse le format des données.

J'espère que ça aide.

237
Scott

C # WebApi PDF téléchargez tout ce qui fonctionne avec l'authentification JS angulaire

Contrôleur Api Web

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

Service JS angulaire

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

Soit on fera

Contrôleur JS angulaire appelant le service 

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

Et enfin la page HTML

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

Cela va être refondu simplement en partageant le code maintenant, j'espère que cela aidera quelqu'un car cela m'a pris un certain temps pour que cela fonctionne.

7
tfa

Pour moi, l'API Web était Rails et le côté client Angular utilisé avec Restangular et FileSaver.js

API Web

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>

Contrôleur angulaire

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

Service angulaire

'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');
    })
  }
});
6
AnkitG

Nous devions également développer une solution qui fonctionnerait même avec des API nécessitant une authentification (voir cet article )

En utilisant AngularJS en quelques mots, voici comment nous l’avons fait:

Étape 1: Créer une directive dédiée

// jQuery needed, uses Bootstrap classes, adjust the path of templateUrl
app.directive('pdfDownload', function() {
return {
    restrict: 'E',
    templateUrl: '/path/to/pdfDownload.tpl.html',
    scope: true,
    link: function(scope, element, attr) {
        var anchor = element.children()[0];

        // When the download starts, disable the link
        scope.$on('download-start', function() {
            $(anchor).attr('disabled', 'disabled');
        });

        // When the download finishes, attach the data to the link. Enable the link and change its appearance.
        scope.$on('downloaded', function(event, data) {
            $(anchor).attr({
                href: 'data:application/pdf;base64,' + data,
                download: attr.filename
            })
                .removeAttr('disabled')
                .text('Save')
                .removeClass('btn-primary')
                .addClass('btn-success');

            // Also overwrite the download pdf function to do nothing.
            scope.downloadPdf = function() {
            };
        });
    },
    controller: ['$scope', '$attrs', '$http', function($scope, $attrs, $http) {
        $scope.downloadPdf = function() {
            $scope.$emit('download-start');
            $http.get($attrs.url).then(function(response) {
                $scope.$emit('downloaded', response.data);
            });
        };
    }] 
});

Étape 2: créer un modèle

<a href="" class="btn btn-primary" ng-click="downloadPdf()">Download</a>

Étape 3: Utilisez-le

<pdf-download url="/some/path/to/a.pdf" filename="my-awesome-pdf"></pdf-download>

Cela rendra un bouton bleu. Lorsque vous cliquez dessus, un PDF sera téléchargé (Attention: le système doit fournir le PDF en codage Base64!) Et le mettre dans le fichier href. Le bouton devient vert et fait basculer le texte sur Enregistrer. L'utilisateur peut cliquer à nouveau et une boîte de dialogue de téléchargement standard pour le fichier my-awesome.pdf s'affichera.

2
aix

Envoyez votre fichier sous forme de chaîne base64.

 var element = angular.element('<a/>');
                         element.attr({
                             href: 'data:attachment/csv;charset=utf-8,' + encodeURI(atob(response.payload)),
                             target: '_blank',
                             download: fname
                         })[0].click();

Si la méthode attr ne fonctionne pas dans Firefox, vous pouvez également utiliser la méthode javaScript setAttribute

1
PPB

J'ai passé en revue toute une gamme de solutions et c'est ce que j'ai trouvé très efficace pour moi.

Dans mon cas, j’avais besoin d’envoyer une demande de publication avec quelques informations de connexion. Mais en valait la peine.

var printPDF = function () {
        //prevent double sending
        var sendz = {};
        sendz.action = "Print";
        sendz.url = "api/Print";
        jQuery('<form action="' + sendz.url + '" method="POST">' +
            '<input type="hidden" name="action" value="Print" />'+
            '<input type="hidden" name="userID" value="'+$scope.user.userID+'" />'+
            '<input type="hidden" name="ApiKey" value="' + $scope.user.ApiKey+'" />'+
            '</form>').appendTo('body').submit().remove();

    }
0
OneGhana

Dans votre composant, c’est-à-dire le code js angulaire:

function getthefile (){
window.location.href='http://localhost:1036/CourseRegConfirm/getfile';
};
0
Shivani Jadhav

Vous pouvez implémenter une fonction showfile qui prend en compte les paramètres des données renvoyées par WEBApi et un nom de fichier pour le fichier que vous essayez de télécharger. Ce que j'ai fait était de créer un service de navigateur distinct qui identifie le navigateur de l'utilisateur, puis gère le rendu du fichier en fonction du navigateur. Par exemple, si le navigateur cible est chrome sur un ipad, vous devez utiliser l'objet javascripts FileReader.

FileService.showFile = function (data, fileName) {
    var blob = new Blob([data], { type: 'application/pdf' });

    if (BrowserService.isIE()) {
        window.navigator.msSaveOrOpenBlob(blob, fileName);
    }
    else if (BrowserService.isChromeIos()) {
        loadFileBlobFileReader(window, blob, fileName);
    }
    else if (BrowserService.isIOS() || BrowserService.isAndroid()) {
        var url = URL.createObjectURL(blob);
        window.location.href = url;
        window.document.title = fileName;
    } else {
        var url = URL.createObjectURL(blob);
        loadReportBrowser(url, window,fileName);
    }
}


function loadFileBrowser(url, window, fileName) {
    var iframe = window.document.createElement('iframe');
    iframe.src = url
    iframe.width = '100%';
    iframe.height = '100%';
    iframe.style.border = 'none';
    window.document.title = fileName;
    window.document.body.appendChild(iframe)
    window.document.body.style.margin = 0;
}

function loadFileBlobFileReader(window, blob,fileName) {
    var reader = new FileReader();
    reader.onload = function (e) {
        var bdata = btoa(reader.result);
        var datauri = 'data:application/pdf;base64,' + bdata;
        window.location.href = datauri;
        window.document.title = fileName;
    }
    reader.readAsBinaryString(blob);
}
0
Erkin Djindjiev