Pregunta La descarga simple de archivos de Angularjs hace que el enrutador redirija


HTML:

<a href="mysite.com/uploads/asd4a4d5a.pdf" download="foo.pdf">

Las subidas obtienen un nombre de archivo único mientras el nombre real se mantiene en la base de datos. Quiero realizar una simple descarga de archivos. Pero el código anterior redirige a / debido a:

$routeProvider.otherwise({
    redirectTo: '/', 
    controller: MainController
});

Lo intenté con

$scope.download = function(resource){
    window.open(resource);
}

pero esto solo abre el archivo en una nueva ventana.

¿Alguna idea de cómo habilitar una descarga real para cualquier tipo de archivo?


75
2018-05-21 13:04


origen


Respuestas:


https://docs.angularjs.org/guide/$location#html-link-rewriting

En casos como el siguiente, los enlaces no se reescriben; en cambio, el   el navegador realizará una recarga de página completa al enlace original.

  • Enlaces que contienen elemento objetivo Ejemplo:
    <a href="/ext/link?a=b" target="_self">link</a> 

  • Enlaces absolutos que van a un dominio diferente Ejemplo:
    <a href="http://angularjs.org/">link</a> 

  • Enlaces que comienzan con '/' que conducen a una ruta base diferente cuando se define base Ejemplo:
    <a href="/not-my-base/link">link</a>

Entonces, en tu caso, deberías agregar un atributo objetivo como ese ...

<a target="_self" href="example.com/uploads/asd4a4d5a.pdf" download="foo.pdf">

111
2018-05-21 13:40



También tuvimos que desarrollar una solución que incluso funcionaría con las API que requieren autenticación (ver Este artículo)

Usando AngularJS en pocas palabras aquí es cómo lo hicimos:

Paso 1: crea una directiva dedicada

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

Paso 2: crea una plantilla

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

Paso 3: usarlo

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

Esto generará un botón azul. Al hacer clic, se descargará un PDF (¡Atención: el servidor debe entregar el PDF en la codificación Base64!) Y colocarlo en el href. El botón se pone verde y cambia el texto a Salvar. El usuario puede hacer clic nuevamente y se le presentará un diálogo de archivo de descarga estándar para el archivo my-awesome.pdf.

Nuestro ejemplo usa archivos PDF, pero aparentemente podría proporcionar cualquier formato binario dado que está codificado correctamente.


32
2017-09-25 07:47



Si necesita una directiva más avanzada, recomiendo la solución que implementé, probada correctamente en Internet Explorer 11, Chrome y FireFox.

Espero que sea útil.

HTML:

<a href="#" class="btn btn-default" file-name="'fileName.extension'"  ng-click="getFile()" file-download="myBlobObject"><i class="fa fa-file-excel-o"></i></a>

DIRECTIVA:

directive('fileDownload',function(){
    return{
        restrict:'A',
        scope:{
            fileDownload:'=',
            fileName:'=',
        },

        link:function(scope,elem,atrs){


            scope.$watch('fileDownload',function(newValue, oldValue){

                if(newValue!=undefined && newValue!=null){
                    console.debug('Downloading a new file'); 
                    var isFirefox = typeof InstallTrigger !== 'undefined';
                    var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
                    var isIE = /*@cc_on!@*/false || !!document.documentMode;
                    var isEdge = !isIE && !!window.StyleMedia;
                    var isChrome = !!window.chrome && !!window.chrome.webstore;
                    var isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
                    var isBlink = (isChrome || isOpera) && !!window.CSS;

                    if(isFirefox || isIE || isChrome){
                        if(isChrome){
                            console.log('Manage Google Chrome download');
                            var url = window.URL || window.webkitURL;
                            var fileURL = url.createObjectURL(scope.fileDownload);
                            var downloadLink = angular.element('<a></a>');//create a new  <a> tag element
                            downloadLink.attr('href',fileURL);
                            downloadLink.attr('download',scope.fileName);
                            downloadLink.attr('target','_self');
                            downloadLink[0].click();//call click function
                            url.revokeObjectURL(fileURL);//revoke the object from URL
                        }
                        if(isIE){
                            console.log('Manage IE download>10');
                            window.navigator.msSaveOrOpenBlob(scope.fileDownload,scope.fileName); 
                        }
                        if(isFirefox){
                            console.log('Manage Mozilla Firefox download');
                            var url = window.URL || window.webkitURL;
                            var fileURL = url.createObjectURL(scope.fileDownload);
                            var a=elem[0];//recover the <a> tag from directive
                            a.href=fileURL;
                            a.download=scope.fileName;
                            a.target='_self';
                            a.click();//we call click function
                        }


                    }else{
                        alert('SORRY YOUR BROWSER IS NOT COMPATIBLE');
                    }
                }
            });

        }
    }
})

EN CONTROLADOR:

$scope.myBlobObject=undefined;
$scope.getFile=function(){
        console.log('download started, you can show a wating animation');
        serviceAsPromise.getStream({param1:'data1',param1:'data2', ...})
        .then(function(data){//is important that the data was returned as Aray Buffer
                console.log('Stream download complete, stop animation!');
                $scope.myBlobObject=new Blob([data],{ type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
        },function(fail){
                console.log('Download Error, stop animation and show error message');
                                    $scope.myBlobObject=[];
                                });
                            }; 

EN SERVICIO:

function getStream(params){
                 console.log("RUNNING");
                 var deferred = $q.defer();

                 $http({
                     url:'../downloadURL/',
                     method:"PUT",//you can use also GET or POST
                     data:params,
                     headers:{'Content-type': 'application/json'},
                     responseType : 'arraybuffer',//THIS IS IMPORTANT
                    })
                    .success(function (data) {
                        console.debug("SUCCESS");
                        deferred.resolve(data);
                    }).error(function (data) {
                         console.error("ERROR");
                         deferred.reject(data);
                    });

                 return deferred.promise;
                };

BACKEND (en PRIMAVERA):

@RequestMapping(value = "/downloadURL/", method = RequestMethod.PUT)
public void downloadExcel(HttpServletResponse response,
        @RequestBody Map<String,String> spParams
        ) throws IOException {
        OutputStream outStream=null;
outStream = response.getOutputStream();//is important manage the exceptions here
ObjectThatWritesOnOutputStream myWriter= new ObjectThatWritesOnOutputStream();// note that this object doesn exist on JAVA,
ObjectThatWritesOnOutputStream.write(outStream);//you can configure more things here
outStream.flush();
return;
}

8
2018-05-20 23:37



en plantilla

<md-button class="md-fab md-mini md-warn md-ink-ripple" ng-click="export()" aria-label="Export">
<md-icon class="material-icons" alt="Export" title="Export" aria-label="Export">
    system_update_alt
</md-icon></md-button>

en el controlador

     $scope.export = function(){ $window.location.href = $scope.export; };

0
2018-03-03 13:34



Sé que esta es una publicación anterior, pero tuve problemas para obtener una solución en el intercambio de pila para una descarga automática con una publicación angular.

Aquí está mi solución (Un híbrido de jquery / Angular / php):

PHP

    return array($filename,$url); 

Vista angular

    <a target="_self" id="downloadpdf" href={{downloadurl}} download={{filename}} style="display: none"></a>

Controlador Angular

una vez que la respuesta se recibe con la url y el nombre del archivo:

     $scope.downloadurl=data[1];
     $scope.filename=data[0];

     setTimeout(function () {
                        $('#downloadpdf')[0].click();
                    }, 1000);  

Puse esto en un retraso de 1 segundo para dar tiempo a que los valores se rellenaran porque a veces se ejecutaba demasiado rápido.

¡Espero eso ayude!


-9
2018-02-14 22:24