AngularJS: як реалізувати просте завантаження файлів із багаточастинною формою?


144

Я хочу зробити просту форму з декількох частин з AngularJS на сервері node.js, форма повинна містити об’єкт JSON в одній частині та зображення в іншій частині (я наразі публікую лише об’єкт JSON з $ ресурсом)

Я подумав, що я повинен почати з введення type = "файл", але потім з'ясував, що AngularJS не може прив'язати до цього ..

всі приклади, які я можу знайти, - це обгортання плагінів jQuery для перетягування та перетягування. Я хочу просто завантажити один файл.

Я новачок у AngularJS і зовсім не відчуваю себе комфортно, коли пишу власні директиви.


1
я думаю, що це може допомогти: noypi-linux.blogspot.com/2013/04/…
Нойпі Гілас

1
Дивіться цю відповідь: stackoverflow.com/questions/18571001/… Безліч варіантів для вже працюючих систем.
Анойз

Відповіді:


188

Справжнє робоче рішення, яке не має інших залежностей, ніж angularjs (тестоване з v.1.0.6)

html

<input type="file" name="file" onchange="angular.element(this).scope().uploadFile(this.files)"/>

Angularjs (1.0.6) не підтримує ng-модель для тегів "input-file", тому вам доведеться робити це "рідним способом", який передає всі (зрештою) вибрані файли від користувача.

контролер

$scope.uploadFile = function(files) {
    var fd = new FormData();
    //Take the first selected file
    fd.append("file", files[0]);

    $http.post(uploadUrl, fd, {
        withCredentials: true,
        headers: {'Content-Type': undefined },
        transformRequest: angular.identity
    }).success( ...all right!... ).error( ..damn!... );

};

Класна частина - це невизначений тип вмісту та transformRequest: angular.identity, які надають на $ http можливість вибору правильного "типу вмісту" та керування межею, необхідною під час обробки даних із багаточастинкою.


2
@Fabio Чи можете у плз пояснити мені, що робить це перетворення запиту? що таке angular.identity? Я стукав головою через день просто для того, щоб здійснити завантаження файлів з кількома частинами.
квітня 1414

1
@RandomUser в додатку Java для відпочинку, щось подібне mkyong.com/webservices/jax-rs/file-upload-example-in-jersey
Fabio Bonfante

2
вау, просто геніально, спасибі велике. Тут я маю завантажити декілька зображень та деякі інші дані, тому я маніпулюю fd якfd.append("file", files[0]); fd.append("file",files[1]); fd.append("name", "MyName")
Мошій

1
console.log (fd) показує, що форма порожня? це так?
Дж. Борн

4
Зауважте, що це не спрацює, якщо ви відключили програму debugInfo (як рекомендовано)
Бруно Перес

42

Ви можете використовувати просту / легку директиву щодо завантаження файлів ng-файлів . Він підтримує перетягування, перебіг файлів та завантаження файлів для браузерів, які не є HTML5, із спалахом FileAPI

<div ng-controller="MyCtrl">
  <input type="file" ngf-select="onFileSelect($files)" multiple>
</div>

JS:

//inject angular file upload directive.
angular.module('myApp', ['ngFileUpload']);

var MyCtrl = [ '$scope', 'Upload', function($scope, Upload) {
  $scope.onFileSelect = function($files) {
  Upload.upload({
    url: 'my/upload/url',
    file: $files,            
  }).progress(function(e) {
  }).then(function(data, status, headers, config) {
    // file is uploaded successfully
    console.log(data);
  }); 

}];

Чи не виконує це один запит POST для кожного файлу одночасно?
Анойз

Так. У трекері випуску github є дискусії про те, чому краще завантажувати файли по одному. Flash API не підтримує надсилання файлів разом, а AFAIK Amazon S3 також не підтримує його.
danial

Отже, ви говорите, що загальний правильніший підхід - це відправлення одноразового POST-запиту? Я бачу в цьому декілька переваг, але також більше проблем при створенні спокійної підтримки на стороні сервера.
Анойз

2
Я реалізую це - завантажувати кожен файл як збереження ресурсу, і сервер збереже його у локальній файловій системі (або базі даних) і поверне унікальний ідентифікатор (тобто випадкова папка / ім'я файлу або ідентифікатор db) для цього файлу. Потім, коли всі завантаження виконано, клієнт надсилає ще один запит PUT / POST, який містить додаткові дані та ідентифікатори файлів, які завантажуються для цього запиту. Тоді сервер збереже запис із пов’язаними файлами. Це як gmail, коли ви завантажуєте файли, а потім надсилаєте електронне повідомлення.
danial

1
Це не "просто / легко". У розділі зразків навіть немає прикладу завантаження лише одного файлу.
Кріс

9

Ефективніше надсилати файл безпосередньо.

Кодування base64 з Content-Type: multipart/form-dataдодає додаткові накладні витрати на 33%. Якщо сервер підтримує його, більш ефективно відправляти файли безпосередньо:

$scope.upload = function(url, file) {
    var config = { headers: { 'Content-Type': undefined },
                   transformResponse: angular.identity
                 };
    return $http.post(url, file, config);
};

При надсиланні POST з об'єктом File важливо встановити 'Content-Type': undefined. Потім метод відправки XHR виявить об'єкт File і автоматично встановить тип вмісту.

Щоб надсилати декілька файлів, див. " Виконання кількох запитів" безпосередньо з списку файлів$http.post


Я подумав, що я повинен почати з введення type = "файл", але потім з'ясував, що AngularJS не може прив'язати до цього ..

<input type=file>Елемент не за замовчуванням робота з директивою нг-моделі . Для цього потрібна спеціальна директива :

Робоча демонстрація директиви "select-ng-files", яка працює з ng-model1

angular.module("app",[]);

angular.module("app").directive("selectNgFiles", function() {
  return {
    require: "ngModel",
    link: function postLink(scope,elem,attrs,ngModel) {
      elem.on("change", function(e) {
        var files = elem[0].files;
        ngModel.$setViewValue(files);
      })
    }
  }
});
<script src="//unpkg.com/angular/angular.js"></script>
  <body ng-app="app">
    <h1>AngularJS Input `type=file` Demo</h1>
    
    <input type="file" select-ng-files ng-model="fileArray" multiple>
    
    <h2>Files</h2>
    <div ng-repeat="file in fileArray">
      {{file.name}}
    </div>
  </body>


$http.post з типом вмісту multipart/form-data

Якщо потрібно відправити multipart/form-data:

<form role="form" enctype="multipart/form-data" name="myForm">
    <input type="text"  ng-model="fdata.UserName">
    <input type="text"  ng-model="fdata.FirstName">
    <input type="file"  select-ng-files ng-model="filesArray" multiple>
    <button type="submit" ng-click="upload()">save</button>
</form>
$scope.upload = function() {
    var fd = new FormData();
    fd.append("data", angular.toJson($scope.fdata));
    for (i=0; i<$scope.filesArray.length; i++) {
        fd.append("file"+i, $scope.filesArray[i]);
    };

    var config = { headers: {'Content-Type': undefined},
                   transformRequest: angular.identity
                 }
    return $http.post(url, fd, config);
};

При надсиланні POST за допомогою API FormData важливо встановити 'Content-Type': undefined. Потім метод передачі XHR виявить FormDataоб'єкт і автоматично встановить заголовок типу вмісту на багаточастинні / форми-дані з відповідним кордоном .


filesInputДиректива не видається , працює з кутовими 1.6.7, я можу бачити файли в ng-repeatале то ж $scope.variableпорожня в контролері? Також один із ваших прикладів використовує file-modelі одинfiles-input
Dan

@Dan я тестував це, і він працює. Якщо у вас виникли проблеми з вашим кодом, вам слід задати нове запитання на прикладі мінімального, повного, перевіреного . Змінено ім'я директиви на select-ng-files. Тестовано на AngularJS 1.7.2.
georgeawg

5

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

var formData = new FormData();

Перейдіть за цим посиланням на блог із інформацією про те, як підтримка обмежена сучасними браузерами, але в іншому випадку це повністю вирішує цю проблему.

В іншому випадку ви можете опублікувати форму в iframe, використовуючи атрибут target. Опублікувавши форму, не забудьте встановити ціль для рамки з її властивістю відображення. Ціль - назва iframe. (Просто щоб ти знав.)

Я сподіваюся, що це допомагає


AFAIK FormData не працює з IE. можливо, краща ідея зробити базове кодування файлу зображення і відправити його в JSON? як я можу прив’язати до вхідного типу = "файл" за допомогою AngularJS, щоб отримати вибраний шлях до файлу?
Гал Бен-Хаїм

3

Я знаю, що це пізній запис, але я створив просту директиву щодо завантаження. Над яким ви зможете працювати за короткий час!

<input type="file" multiple ng-simple-upload web-api-url="/api/post"
       callback-fn="myCallback" />

ng-simple - завантажуйте більше на Github із прикладом за допомогою Web API.


2
чесно кажучи, пропонування копіювання та вставки коду у ваш проект readme може бути великою чорною позначкою. спробуйте інтегрувати ваш проект із загальними менеджерами пакетів, наприклад, npm або bower.
Стефано Торресі

2

Ви можете завантажити через $resource, призначивши дані атрибуту params ресурсу, actionsнаприклад:

$scope.uploadFile = function(files) {
    var fdata = new FormData();
    fdata.append("file", files[0]);

    $resource('api/post/:id', { id: "@id" }, {
        postWithFile: {
            method: "POST",
            data: fdata,
            transformRequest: angular.identity,
            headers: { 'Content-Type': undefined }
        }
    }).postWithFile(fdata).$promise.then(function(response){
         //successful 
    },function(error){
        //error
    });
};

1

Я щойно написав просту директиву (з існуючого курсу) для простого завантажувача в AngularJs.

(Точний плагін для завантаження jQuery https://github.com/blueimp/jQuery-File-Upload )

Простий завантажувач, що використовує AngularJs (з реалізацією CORS)

(Хоча сторона сервера призначена для PHP, ви також можете просто змінити її вузол)


1
Не забудьте відзначити, що ви не даєте часу закривати \ відповідати на запитання у вашій репо
Олег Білоусов

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