Télécharger un fichier à l'aide de ResponseEntity avec angular ne fonctionne pas

J'ai besoin de télécharger un fichier en utilisant angular côté client, ce fichier peut avoir n'importe quel format, il peut s'agir d'un pdf ou excel ou image ou txt ... ma méthode fonctionne uniquement pour les fichiers txt et me donne un format d'échec pour excel et image et pour le pdf, il donne un pdf vide.

Donc dans mon contrôleur, voici la fonction qui appelle la méthode de service:

    vm.downloadFile = downloadFile;

    function downloadFile(file){
        var urlDir = "C://STCI//"+idpeticion;
        return VerDocServices.downloadFile(file,urlDir)
          .then(function(response) {
            var data = response.data;
            var filename = file;
            var contentType = 'application/octet-stream';//octet-stream             
            var linkElement = document.createElement('a');
            try {
                var blob = new Blob([ data ], {
                    type : contentType
                });
                var url = window.URL.createObjectURL(blob);
                linkElement.setAttribute('href', url);
                linkElement.setAttribute("download", filename);
                var clickEvent = new MouseEvent("click", {
                    "view" : window,
                    "bubbles" : true,
                    "cancelable" : false
                });
                linkElement.dispatchEvent(clickEvent);
            } catch (ex) {
                console.log(ex);
                throw ex;
            }
        }).catch(function(response) {
            alert('Se ha producido un error al exportar del documento');
            console.log(response.status);
            throw response;
        });
    }

Et mon service.js a:

angular.module('mecenzApp').service('VerDocServices',['$http',function($http) {

this.downloadFile = function(file,urlDir) {

    return $http.get('api/downloadFile', {
        params : {
            file : file,
            urlDir : urlDir
        }
    }); }} ]);

Et ma méthode de service est la suivante:

@GetMapping("/downloadFile")
@Timed
public ResponseEntity<byte[]> downloadFile(@RequestParam(value = "file") String file, @RequestParam(value = "urlDir") String urlDir) {
    log.debug("GET ---------------- DOWNLOAD FILE : {}", file);
    log.debug("GET ---------------- From the DIRECTORY: {}",urlDir);

    InputStream fileStream;
    String filepath = urlDir+File.separator+file;
    try {
        File f = new File(filepath);
        log.debug("GET ---------------- FILE: {}",f.getPath());
        fileStream = new FileInputStream(f);
        byte[] contents = IOUtils.toByteArray(fileStream);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/octet-stream"));   
        String filename = file;
        headers.setContentDispositionFormData(filename, filename);
        ResponseEntity<byte[]> response2 = new ResponseEntity<byte[]>(contents, headers, HttpStatus.OK);
        fileStream.close();
        return response2;            
        
    } catch (FileNotFoundException e) {
       System.err.println(e);
    } catch (IOException e) {
        System.err.println(e);
    }
   return null;
}

Pourriez-vous s'il vous plaît jeter un oeil et me dire ce que j'ai manqué ??

Merci :)

3
Smahane 16 janv. 2017 à 12:01

2 réponses

Meilleure réponse

Comment télécharger des fichiers binaires avec AngularJS

Lors du téléchargement de fichiers binaires, il est important de définir le responseType:

app.service('VerDocServices',['$http',function($http) {

    this.downloadFile = function(url, file, urlDir) {
        var config = {
            //SET responseType
            responseType: 'blob',
            params : {
                file : file,
                urlDir : urlDir
            }
         };

        return $http.get(url, config)
          .then(function(response) {
            return response.data;
        }).catch(function(response) {
            console.log("ERROR: ", response.status);
            throw response;
        }); 
    }; 
}]);

Si responseType est omis, l'API XHR utilise par défaut la conversion UTF-8 encodé texte à DOMString (UTF-16) qui corrompra le PDF, image et autres fichiers binaires.

Pour plus d’informations, voir MDN Web API Reference - XHR ResponseType

6
georgeawg 28 août 2017 à 14:45

Je ne sais pas grand-chose sur le backend, mais je vais fournir ce que j'ai utilisé peut-être que cela aidera, donc sur le fichier Java Script:

//your $http(request...)

.success(function (data, status, headers, config) {
  //Recieves base64 String data
  var fileName = 'My Awesome File Name'+'.'+'pdf';

  //Parsing base64 String...
  var binaryString =  window.atob(data);
  var binaryLen = binaryString.length;
  var fileContent = new Uint8Array(binaryLen);
  for (var i = 0; i < binaryLen; i++)        {
     var ascii = binaryString.charCodeAt(i);
     fileContent[i] = ascii;
  }
  var blob = new Blob([fileContent], { type: 'application/octet-stream' }); //octet-stream
  var fileURL = window.URL.createObjectURL(blob);
  $sce.trustAsResourceUrl(fileURL);  //allow angular to trust this url
  //Creating the anchor download link
  var anchor = angular.element('<a/>');
  anchor.css({display: 'none'}); // Make sure it's not visible
  angular.element(document.body).append(anchor); // Attach it to the document
  anchor.attr({
    href: fileURL,
    target: '_blank',
    download: fileName
  })[0].click();
  anchor.remove(); // Clean it up afterwards
})
//.error(function(...

Et sur votre backend, assurez-vous que votre service Web produit un flux d'octets et renvoie le fichier au format de données base64, j'ai fait cela en utilisant Java JAX-RS comme ceci:

@POST
@Path("/downloadfile")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response downloadFile(...){
  String base64String = Base64.getEncoder().encodeToString(/*here you pass your file in byte[] format*/);
  return Response.ok(base64String).build();
}
1
Mostafa Omar 16 janv. 2017 à 09:52