AngularJS $http-post - conversion de binaire en fichier excel et de le télécharger

j'ai créé une application dans Angular JS pour télécharger un classeur Excel par $http post.

dans le code ci-dessous , je passe l'information sous la forme de JSON, et je l'envoie au serveur REST web service (java) par le biais d'un $http POST angulaire. Le service web utilise l'information du JSON et produit un classeur Excel. Dans la réponse dans le corps de succès de $ http post, je reçois des données binaires dans ce données variable, mais ne savent pas comment le convertir et le télécharger comme un fichier Excel.

est-ce que quelqu'un peut s'il vous plaît me dire une solution pour cela pour convertir le binaire En fichier Excel et télécharger?

Mon code est donné ci-dessous:

$http({
        url: 'myweb.com/myrestService',
        method: "POST",
        data: json, //this is your json data string
        headers: {
           'Content-type': 'application/json'
        }
    }).success(function (data, status, headers, config) {

        // Here i'm getting excel sheet binary datas in 'data' 

    }).error(function (data, status, headers, config) {

    });
51
demandé sur Edward Brey 2014-03-17 10:08:24

9 réponses

vient de remarquer que vous ne pouvez pas l'utiliser à cause de IE8/9 mais je vais pousser soumettre de toute façon... peut-être que quelqu'un le trouve utile

cela peut en fait être fait via le navigateur, en utilisant blob . Notez le responseType et le code dans la promesse success .

$http({
    url: 'your/webservice',
    method: "POST",
    data: json, //this is your json data string
    headers: {
       'Content-type': 'application/json'
    },
    responseType: 'arraybuffer'
}).success(function (data, status, headers, config) {
    var blob = new Blob([data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
    var objectUrl = URL.createObjectURL(blob);
    window.open(objectUrl);
}).error(function (data, status, headers, config) {
    //upload failed
});

Il ya quelques problèmes avec elle bien que comme:

  1. Il ne supporte pas les IE 8 et 9 :
  2. il ouvre une fenêtre pop up pour ouvrir le objectUrl que les gens auraient pu bloquer
  3. génère des noms de fichiers bizarres

Il a fait un travail!

blob Le code côté serveur en PHP que j'ai testé ressemble à ceci. Je suis sûr que vous pouvez définir des en-têtes similaires en Java:

$file = "file.xlsx";
header('Content-disposition: attachment; filename='.$file);
header('Content-Length: ' . filesize($file));
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate');
header('Pragma: public');
echo json_encode(readfile($file));

Edit 20.04.2016

Les navigateurs

rendent la sauvegarde plus difficile les données de cette façon. Une bonne option est d'utiliser filesaver.js . Il fournit une implémentation de navigateur croisé pour saveAs , et il devrait remplacer une partie du code dans le success promesse ci-dessus.

77
répondu Jorg 2016-04-20 02:08:58

C'est comment vous le faites:

  1. Oublier IE8/IE9, il n'est pas utile de l'effort et ne pas payer le retour de l'argent.
  2. vous devez utiliser le bon en-tête HTTP,utilisez Accept to 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' et aussi vous avez besoin de mettre responsietype à 'arraybuffer'(ArrayBuffer mais mis avec lowercase).
  3. HTML5 saveAs est utilisé pour enregistrer les données réelles à votre format voulu. Note qu'il encore travailler sans ajouter de type dans ce cas.
$http({
    url: 'your/webservice',
    method: 'POST',
    responseType: 'arraybuffer',
    data: json, //this is your json data string
    headers: {
        'Content-type': 'application/json',
        'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    }
}).success(function(data){
    var blob = new Blob([data], {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    });
    saveAs(blob, 'File_Name_With_Some_Unique_Id_Time' + '.xlsx');
}).error(function(){
    //Some error log
});

Conseil! Ne pas mélanger le "et", s'en tenir à toujours utiliser ', dans un environnement professionnel, vous devrez passer js de validation par exemple jshint, en va de même pour l'utilisation de === et non ==, et ainsi de suite, mais c'est un autre sujet :)

je mettrais le Save excel dans un autre service, de sorte que vous avez la structure propre et le poste est dans un service approprié de ses propres. Je peux fais un js fiddle pour toi, si tu ne fais pas marcher mon exemple. Ensuite, j'aurais aussi besoin de quelques données json de votre part que vous utilisez pour un exemple complet.

Heureux de codage.. Eduardo

24
répondu Eduardo in Norway 2014-08-22 21:52:22

télécharger la réponse du serveur comme un tampon de tableau. Stockez-le comme un Blob en utilisant le type de contenu du serveur (qui devrait être application/vnd.openxmlformats-officedocument.spreadsheetml.sheet ):

var httpPromise = this.$http.post(server, postData, { responseType: 'arraybuffer' });
httpPromise.then(response => this.save(new Blob([response.data],
    { type: response.headers('Content-Type') }), fileName));

Enregistrer le blob de l'appareil de l'utilisateur:

save(blob, fileName) {
    if (window.navigator.msSaveOrOpenBlob) { // For IE:
        navigator.msSaveBlob(blob, fileName);
    } else { // For other browsers:
        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = fileName;
        link.click();
        window.URL.revokeObjectURL(link.href);
    }
}
8
répondu Edward Brey 2017-01-10 00:19:02

a travaillé pour moi -

$scope.downloadFile = function () {
        Resource.downloadFile().then(function (response) {
            var blob = new Blob([response.data], { type: "application/pdf" });
            var objectUrl = URL.createObjectURL(blob);
            window.open(objectUrl);
        },
        function (error) {
            debugger;
        });
    };

qui appelle ce qui suit de mon usine de ressource -

  downloadFile: function () {
           var downloadRequst = {
                method: 'GET',
                url: 'http://localhost/api/downloadFile?fileId=dfckn4niudsifdh.pdf',
                headers: {
                    'Content-Type': "application/pdf",
                    'Accept': "application/pdf"
                },
                responseType: 'arraybuffer'
            }

            return $http(downloadRequst);
        }

assurez - vous que votre API définit le type de contenu de l'en-tête -

        response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
        response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
4
répondu Jonathan Forster 2016-10-05 14:04:22

il n'y a aucun moyen (à ma connaissance) de déclencher la fenêtre de téléchargement dans votre navigateur à partir de Javascript. La seule façon de le faire est de rediriger le navigateur vers une url qui transmet le fichier dans le navigateur.

si vous pouvez modifier votre service de repos, vous pourriez être en mesure de le résoudre en changeant de sorte que la requête POST ne répond pas avec le fichier binaire, mais avec une url à ce fichier. Cela vous donnera l'url en Javascript au lieu des données binaires, et vous pouvez rediriger le navigateur à cette url, qui devrait inciter le téléchargement sans quitter la page d'origine.

3
répondu Anders Ekdahl 2014-03-17 06:55:53

réponse n ° 5 a fonctionné pour moi ,Suggestion au développeur qui sont confrontés à un problème similaire.

//////////////////////////////////////////////////////////
//Server side 
//////////////////////////////////////////////////////////
imports ***
public class AgentExcelBuilder extends AbstractExcelView {

protected void buildExcelDocument(Map<String, Object> model,
            HSSFWorkbook workbook, HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        //poi code goes here ....

        response.setHeader("Cache-Control","must-revalidate");
        response.setHeader("Pragma", "public");
        response.setHeader("Content-Transfer-Encoding","binary");
        response.setHeader("Content-disposition", "attachment; filename=test.xls");

        OutputStream output = response.getOutputStream();

        workbook.write(output);
        System.out.println(workbook.getActiveSheetIndex());
        System.out.println(workbook.getNumberOfSheets());
        System.out.println(workbook.getNumberOfNames());
        output.flush();
        output.close(); 
}//method buildExcelDocument ENDS

//service.js at angular JS code
function getAgentInfoExcel(workgroup,callback){
        $http({
            url: CONTEXT_PATH+'/rest/getADInfoExcel',
            method: "POST",
            data: workgroup, //this is your json data string
            headers: {
               'Content-type': 'application/json'
            },
            responseType: 'arraybuffer'
        }).success(function (data, status, headers, config) {
            var blob = new Blob([data], {type: "application/vnd.ms-excel"});
            var objectUrl = URL.createObjectURL(blob);
            window.open(objectUrl);
        }).error(function (data, status, headers, config) {
            console.log('Failed to download Excel')
        });
    }
////////////////////////////////in .html 

<div class="form-group">`enter code here`
                                <a href="javascript:void(0)" class="fa fa-file-excel-o"
                                    ng-click="exportToExcel();"> Agent Export</a>
                            </div>
0
répondu Vj Singh 2016-01-19 10:46:52

Vous pourriez aussi bien prendre une approche alternative -- vous n'avez pas à utiliser $http, vous n'avez pas besoin de bibliothèques supplémentaires, et il faut travailler dans n'importe quel navigateur.



Il suffit de placer un formulaire invisible sur votre page.

<form name="downloadForm" action="/MyApp/MyFiles/Download" method="post" target="_self">
    <input type="hidden" name="value1" value="{{ctrl.value1}}" />
    <input type="hidden" name="value2" value="{{ctrl.value2}}" />
</form>

et placez ce code dans votre contrôleur angulaire.

ctrl.value1 = 'some value 1';  
ctrl.value2 = 'some value 2';  
$timeout(function () {
    $window.document.forms['downloadForm'].submit();
});

ce code affichera vos données à / MyApp/MyFiles / télécharger et vous recevrez un fichier dans vos téléchargements dossier.

il fonctionne avec Internet Explorer 10.



Si un formulaire HTML conventionnel ne vous permet pas d'afficher votre objet complexe, alors vous avez deux options:



1. Stringifiez votre objet et mettez-le dans l'un des champs de la forme comme une corde.

<input type="hidden" name="myObjJson" value="{{ctrl.myObj | json:0}}" />



2. Considérons les formulaires HTML JSON: https://www.w3.org/TR/html-json-forms /

0
répondu Evgeny Nikitin 2016-05-26 14:05:55

j'ai créé un service qui le fera pour vous.

passer dans un objet standard $http , et ajouter quelques paramètres supplémentaires.

1) un paramètre" type". Spécifiant le type de fichier que vous récupérez. Par défaut à: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'

2) un paramètre "nom de fichier". Ceci est nécessaire, et devrait inclure l'extension.

exemple:

httpDownloader({
  method : 'POST',
  url : '--- enter the url that returns a file here ---',
  data : ifYouHaveDataEnterItHere,
  type : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // this is the default
  fileName : 'YourFileName.xlsx'
}).then(res => {}).catch(e => {});

C'est tout ce dont tu as besoin. Le le fichier sera téléchargé sur l'appareil de l'utilisateur sans popup.

voici le git repo: https://github.com/stephengardner/ngHttpDownloader

0
répondu Augie Gardner 2017-06-07 04:05:03

je faisais face à ce même problème. Laissez-moi vous dire comment je l'ai résolu et réalisé tout ce que vous semblez vouloir.

exigences:

  1. Doit avoir un bouton (ou un lien) vers un fichier ou d'un flux de mémoire)
  2. doit cliquer sur le bouton et faire télécharger le fichier

dans mon service, (j'utilise Asp.net Web API), j'ai un controller qui renvoie un "HttpResponseMessage". J'ajoute un "StreamContent" à la réponse.Champ de contenu, définissez les en-têtes à "application / octet-stream" et ajoutez les données en pièce jointe. Je lui donne même un nom "myAwesomeFile.xlsx "

response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StreamContent(memStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "myAwesomeFile.xlsx" };

maintenant voici le truc;)

je stocke l'URL de base dans un fichier texte que j'ai lu dans une variable dans une valeur angulaire appelée"apiRoot". Je le fais en la déclarant, puis sur la fonction "exécuter" du Module, comme:

app.value('apiRoot', { url: '' });
app.run(function ($http, apiRoot) {
    $http.get('/api.txt').success(function (data) {
        apiRoot.url = data;
    });
});

de cette façon, je peux définir L'URL dans un fichier texte sur le serveur et ne pas me soucier de" souffler " dans un téléchargement. (Vous pouvez toujours le changer plus tard pour des raisons de sécurité - mais la frustration de développement ;) )

et maintenant la magie:

Tout ce que je fais, c'est créer un lien avec une URL qui va directement à mon terminal de service et la cible est un"_blank".

<a ng-href="{{vm.getFileHref(FileId)}}" target="_blank" class="btn btn-default">&nbsp;Excel File</a>

le secret la sauce est la fonction qui définit le href. Tu es prêt pour ça?

vm.getFileHref = function (Id) {
    return apiRoot.url + "/datafiles/excel/" + Id;
}

Oui, c'est ça. ;)

même dans une situation où vous itérez de nombreux enregistrements qui ont des fichiers à télécharger, vous n'avez qu'à transmettre L'Id à la fonction et la fonction génère l'url au point de service qui délivre le fichier.

Espérons que cette aide!

-2
répondu user1628627 2017-01-16 05:43:38