Як ви подаєте файл для завантаження за допомогою AngularJS або Javascript?


96

У мене є текст у прихованій текстовій області. Коли натискається кнопка, я хотів би, щоб текст пропонувався для завантаження як .txtфайл. Чи можливо це за допомогою AngularJS або Javascript?


1
Які браузери ви підтримуєте? Це можна вирішити деякими творчими способами (наприклад, data-uris, краплі, API історії браузера тощо), але це насправді залежить.
Бенджамін Груенбаум

Angular File Saver - це хороший поліфіл для менш сучасних браузерів.
georgeawg

Відповіді:


110

Ви можете зробити щось подібне за допомогою Blob.

<a download="content.txt" ng-href="{{ url }}">download</a>

у вашому контролері:

var content = 'file content for example';
var blob = new Blob([ content ], { type : 'text/plain' });
$scope.url = (window.URL || window.webkitURL).createObjectURL( blob );

щоб увімкнути URL-адресу:

app = angular.module(...);
app.config(['$compileProvider',
    function ($compileProvider) {
        $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|file|blob):/);
}]);

Будь ласка, зверніть увагу, що

Кожного разу, коли ви викликаєте createObjectURL (), створюється нова URL-адреса об’єкта, навіть якщо ви вже створили одну для того самого об’єкта. Кожен із них потрібно звільнити, викликавши URL.revokeObjectURL (), коли вони вам більше не потрібні. Браузери випускають їх автоматично, коли документ вивантажується; однак для оптимальної роботи та використання пам'яті, якщо є безпечні часи, коли ви можете явно їх вивантажити, вам слід це зробити.

Джерело: MDN


3
Сучасні браузери & IE10 +
dave1010

@thriqon wow firefox + хром дійсно показує інших там!
JonnyRaa

Чудове рішення, але $scope.urlмені не вдалося. Мені довелося використовувати window.locationзамість цього.
Густаво Штраубе,

7
Я помітив, що тоді прив’язний тег є префіксом небезпечним. Для того, щоб обійти це, вам потрібно додати 'blob' до білого списку в app.js, використовуючи $ compileProvider `.config (['$ compileProvider', function ($ compileProvider) {$ compileProvider.aHrefSanitizationWhitelist (/ ^ \ s * (https? | ftp | mailto | tel | файл | blob): /);} ` docs.angularjs.org/api/ng/provider/$compileProvider
coderman

10
downloadАтрибут не підтримуються в IE будь-якої версії або Safari , хоча caniuse.com/#feat=download
Аарон

33

Просто натисніть кнопку для завантаження за допомогою наступного коду.

в HTML

<a class="btn" ng-click="saveJSON()" ng-href="{{ url }}">Export to JSON</a>

У контролері

$scope.saveJSON = function () {
			$scope.toJSON = '';
			$scope.toJSON = angular.toJson($scope.data);
			var blob = new Blob([$scope.toJSON], { type:"application/json;charset=utf-8;" });			
			var downloadLink = angular.element('<a></a>');
                        downloadLink.attr('href',window.URL.createObjectURL(blob));
                        downloadLink.attr('download', 'fileName.json');
			downloadLink[0].click();
		};


1
@Amrut працював на мене за потреби, але чи можете ви пояснити код?
Суворий Дафтарі

Подобається це рішення! При отриманні даних з сервера, наприклад , з використанням $http.get(...)переконайтеся , що встановили , responseType:'arraybuffer'як описано тут: stackoverflow.com/questions/21628378 / ...
Tim Buthe

Працює в Chrome (Win), але Safari (Mac) просто відкриває розмитий файл у браузері. (blob: https / ...) Начебто це рішення дозволяє мені чекати, коли мої обіцянки будуть вирішені.
sekky

26

Спробуйте це

<a target="_self" href="mysite.com/uploads/ahlem.pdf" download="foo.pdf">

та відвідайте цей сайт, це може бути для вас корисним :)

http://docs.angularjs.org/guide/


7
Остерігайтеся downloadатрибута, який досі не підтримується жодною версією IE та Safari. Ознайомтесь тут: caniuse.com/#feat=download
П’єр-Адрієн Буйсон

Коли я використовую його з angular, він приймає url до $ urlRouterProvider і перенаправляє на мою сторінку за замовчуванням, чи є рішення для завантаження файлу замість навігації
HardikDG

Я завжди вважаю покровительством, коли хтось публікує таке посилання.
slugmandrew

22

Це можна зробити у javascript без необхідності відкривати інше вікно браузера.

window.location.assign('url');

Замініть "url" посиланням на ваш файл. Ви можете помістити це у функцію і викликати це, ng-clickякщо вам потрібно ініціювати завантаження за допомогою кнопки.


2
Він замінює сайт на Pdf doc замість цього, щоб відобразити діалогове вікно завантаження.
fdrv

Дякую! Працює як шарм.
Сагі

14

У нашому поточному проекті на роботі у нас був невидимий iFrame, і мені довелося подати URL-адресу для файлу в iFrame, щоб отримати діалогове вікно завантаження. Після натискання кнопки контролер генерує динамічну URL-адресу та запускає подія $ range, де directiveперерахований я написаний користувачем список. Директива додасть iFrame до тіла, якщо він вже не існує, і встановить на нього атрибут url.

EDIT: Додавання директиви

appModule.directive('fileDownload', function ($compile) {
    var fd = {
        restrict: 'A',
        link: function (scope, iElement, iAttrs) {

            scope.$on("downloadFile", function (e, url) {
                var iFrame = iElement.find("iframe");
                if (!(iFrame && iFrame.length > 0)) {
                    iFrame = $("<iframe style='position:fixed;display:none;top:-1px;left:-1px;'/>");
                    iElement.append(iFrame);
                }

                iFrame.attr("src", url);


            });
        }
    };

    return fd;
});

Ця директива відповідає на виклик події контролера downloadFile

так у вашому контролері ви робите

$scope.$broadcast("downloadFile", url);

1
Фрагмент коду буде дуже корисним.
Sudhir N

Наведена вище директива не працює для мене, коли я виводжу створення iframe поза межі області. $
On

Так $ scope. $ Трансляція працює лише на дітей. Ви можете розмістити директиву в області верхнього рівня, якщо це можливо.
Кетан

12

Ви можете встановити location.hrefв даних URI , що містить дані , які ви хочете , щоб дозволити завантаження користувача. Окрім цього, я не думаю, що існує спосіб зробити це лише за допомогою JavaScript.


Для мене це чудово працює, і було набагато чистіше, ніж усі інші речі, які ми намагалися, або IMHO, складні підходи, рекомендовані вище. Angular повністю ігнорує це. Або ви також можете використовувати window.open () / $ window.open (), якщо хочете спробувати відкрити в іншому вікні. Але ви натрапите на блокувальники спливаючих вікон у сучасних браузерах ...
XML

1
У Angular 1.3 $location.hrefзмінено на$window.location.href
igortg

Це чудова відповідь. Наступне посилання на SO надає повний робочий приклад: stackoverflow.com/a/30889331/1625820
herrtim

7

Я хотів би лише додати, що на випадок, якщо файл не завантажиться через небезпечний: blob: null ... коли ви наводите курсор на кнопку завантаження, ви повинні його продезінфікувати. Наприклад,

var app = angular.module ('app', []);

app.config (функція ($ compileProvider) {

$compileProvider.aHrefSanitizationWhitelist(/^\s*(|blob|):/);

5

Якщо у вас є доступ до сервера, розгляньте налаштування заголовків, як відповіли в цьому більш загальному питанні .

Content-Type: application/octet-stream
Content-Disposition: attachment;filename=\"filename.xxx\"

Читаючи коментарі до цієї відповіді, доцільно використовувати більш конкретний тип вмісту, ніж октет-потік.


4

У мене була та сама проблема, і я витрачав багато годин на пошук різних рішень, і тепер я приєднуюсь до всіх коментарів у цьому дописі. Сподіваюся, це буде корисним, моя відповідь була правильно перевірена в Internet Explorer 11, Chrome та FireFox.

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>

ДИРЕКТИВА:

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

        }
    }
})

У КОНТРОЛЕРІ:

$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=[];
                                });
                            }; 

В СЛУЖБІ:

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

БЕКЕНД (на ВЕСНУ):

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

Дякую, це справді працювало для мене, тим більше, що мені потрібні великі передачі файлів.
Marcos Paulo SUS

3

Це працювало для мене в кутовому:

var a = document.createElement("a");
a.href = 'fileURL';
a.download = 'fileName';
a.click();

Якщо у вас є рядок, який ви хочете завантажити, просто змініть файлURL наdata:text/plain;base64,${btoa(theStringGoesHere)}
Курячий суп

2

Я не хотів статичну URL-адресу. У мене є AjaxFactory для виконання всіх операцій ajax. Я отримую URL-адресу із заводу та прив’язую її наступним чином.

<a target="_self" href="{{ file.downloadUrl + '/' + order.OrderId + '/' + fileName }}" download="{{fileName}}">{{fileName}}</a>

Дякую @AhlemMustapha

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.