AngularJS Загрузить несколько файлов с API FormData

Мне нужно загрузить файлы изображений и видео на сервер в приложении Angular, используя Laravel 5.1 в качестве серверной части. Все Ajax-запросы должны сначала идти к контроллеру Laravel, и у нас есть код для обработки файла, когда он туда попадает. Раньше мы делали обычные HTML-формы для отправки файлов на контроллер, но в этом случае нам нужно избегать обновления страницы формы, поэтому я пытаюсь сделать это в Ajax через Angular.

Какую информацию мне нужно отправить на контроллер Laravel с помощью Ajax, которая ранее отправлялась на контроллер через HTML-форму?

Это код в контроллере Laravel, который обрабатывает информацию о файле, как только она туда попадает. Это то, что мне нужно, чтобы выяснить, как отправить, поэтому я надеюсь, что смогу повторно использовать этот код:

    $promotion = Promotion::find($id);
    if (Input::hasFile('img_path')){
        $path             = public_path().'/images/promotion/'.$id.'/';
        $file_path        = $path.'promotion.png';
        $delete           = File::delete($file_path);
        $file             = Input::file('img_path');
        $uploadSuccess    = $file->move($path, 'promotion.png');
        $promotion->img_path = '/images/promotion/'.$id.'/promotion.png';
    }
    if (Input::hasFile('video_path')){
        $path             = public_path().'/video/promotion/'.$id.'/';
        $file_path        = $path.'promotion.mp4';
        $delete           = File::delete($file_path);
        $file             = Input::file('video_path');
        $uploadSuccess    = $file->move($path, 'promotion.mp4');
        $promotion->video_path = '/video/promotion/'.$id.'/promotion.mp4';
    }

Как вы можете видеть выше, мы конвертируем любой полученный файл в PNG с именем файла Promotion.png, чтобы его было легко получить, и мы принимаем только формат видео .mp4. Из-за этого нам не нужно беспокоиться о том, существует ли файл и можно ли его перезаписать. Вот почему вы можете видеть в коде, что мы удаляем любой существующий файл с таким именем перед сохранением.

HTML был просто вводом типа «файл:

<input type="file" id="img_path" name="img_path" class="promo-img-path" accept="image/*">

Сейчас мы используем Angular, поэтому я больше не могу просто отправить это через HTML-форму. Вот что мне нужно, чтобы понять, как это сделать.

Мы два разработчика, которые просто делают все возможное, поэтому я уверен, что есть лучший способ сделать это. Однако, прежде чем я рефакторинг всего этого, я надеюсь, что смогу использовать Angular (или jQuery в крайнем случае), чтобы просто отправить контроллеру любые данные файла, которые нужны Laravel, чтобы приведенный выше код работал. Ответ может быть таким же простым, как «отправить PUT методу в этом контроллере выше, но вместо обычной полезной нагрузки JSON используйте информацию о файле в этом формате, и вы можете собрать эту информацию с помощью…»

Я также был бы признателен за любые советы о том, как лучше я могу сделать это в будущем.


person SpaceNinja    schedule 19.01.2017    source источник
comment
Лучший совет, который я могу дать, — не изобретать велосипед. Существует множество существующих модулей Angular для загрузки файлов — использование одного из них обычно избавляет вас от многих проблем.   -  person Matthew Daly    schedule 19.01.2017
comment
Но все они загружаются напрямую куда-то — они не предназначены для отправки данных файла на контроллер Laravel.   -  person SpaceNinja    schedule 19.01.2017
comment
Вы отправляете файлы через PUT вместе с обычным вводом?   -  person Matthew Daly    schedule 19.01.2017
comment
Оно было отправлено в обычной ‹форме› указанному выше контролеру. Теперь мне нужно использовать Ajax PUT через Angular. Мне просто нужно знать, как отправить все, что нужно этому контроллеру, в PUT, чтобы имитировать все, что отправляется из ‹input type=file›, как в HTML выше...   -  person SpaceNinja    schedule 19.01.2017
comment
Если я правильно помню, массив $_FILE не заполняется для запроса PUT в PHP. Вам нужно использовать POST, но вы можете установить дополнительный параметр в запросе _method=PUT, чтобы сообщить Laravel, что его следует рассматривать как запрос PUT.   -  person Matthew Daly    schedule 19.01.2017
comment
Хорошо, я уверен, что это может быть POST. Итак, вы говорите, что мне нужно включить массив $_FILE в POST, и он будет работать с указанным выше контроллером?   -  person SpaceNinja    schedule 19.01.2017
comment
Я не могу гарантировать, что это проблема, но я сталкивался с этим раньше, и это звучит вероятно. Я бы попробовал отправить файл напрямую в контроллер с помощью Postman и посмотреть, работает ли добавление параметра _method и его отправка через POST.   -  person Matthew Daly    schedule 20.01.2017
comment
Несмотря на это, мне нужно создать вызов Ajax в Angular (не jQuery), чтобы отправить содержимое файла (или данные файла или что-то еще) на указанный выше контроллер Laravel. Работает ли это в Postman или нет, мне нужно сделать это в Angular. Если кто-нибудь может предложить синтаксис для этого, я был бы очень признателен. Все, что я вижу в Интернете, — это либо директива, сохраняющая файл (не то, что мне нужно), либо jQuery (не то, что мне нужно).   -  person SpaceNinja    schedule 20.01.2017
comment
Причина, по которой я предложил попробовать это в Postman, заключается в том, что это укажет, связана ли проблема с самим JavaScript или с тем, как он отправляется. Если вы не можете найти источник проблемы, вы не можете ее исправить. Postman — это инструмент отладки, а не то, что вы используете в производстве.   -  person Matthew Daly    schedule 20.01.2017
comment
Что ж, я ценю руководство. Я все еще на стадии идеи того, как я буду это кодировать. Я никогда раньше не отправлял данные изображения через Ajax на контроллер Laravel, поэтому я не знаю, как будет выглядеть POST. Вы упомянули $_FILE, но поиск возвращает статьи с jQuery. Некоторые упоминают FormData, я изучаю это. Но мне кажется, что ответ должен быть простым, я думаю, это очень необычно.   -  person SpaceNinja    schedule 20.01.2017
comment
$_FILE — это массив, в котором PHP хранит любые отправленные файлы. Если вы используете фреймворк, вы, как правило, не работаете с ним напрямую, поскольку фреймворк обычно предоставляет другой способ. Я бы сначала построил маршрут для получения изображения и убедился, что вы можете отправить изображение, используя что-то вроде Postman. Как только это будет сделано, вам просто нужно сделать точно такой же запрос через AJAX.   -  person Matthew Daly    schedule 20.01.2017


Ответы (1)


Как отправить данные формы с помощью службы $http

При использовании FormData API для файлов и данных POST важно установить заголовок Content-Type до undefined.

var fd = new FormData()
for (var i in $scope.files) {
    fd.append("fileToUpload", $scope.files[i]);
}
var config = {headers: {'Content-Type': undefined}};

var httpPromise = $http.post(url, fd, config);

По умолчанию платформа AngularJS использует тип контента application/json. Установив Content-Type: undefined, платформа AngularJS опускает заголовок типа контента, разрешая XHR API для установки типа контента. При отправке объекта FormData API XHR устанавливает содержимое введите multipart/form-data с правильными границами и кодировкой base64.

Для получения дополнительной информации см. Справочник по веб-API MDN — метод отправки XHR< /а>


Как вы получили информацию о файле в $scope.files?

Как включить <input type="file"> для работы с ng-model

Эта директива также позволяет <input type="file"> автоматически работать с директивами ng-change и ng-form.

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

angular.module("app").directive("selectFilesNg", 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-files-ng ng-model="fileArray" multiple>

    <code><table ng-show="fileArray.length">
    <tr><td>Name</td><td>Date</td><td>Size</td><td>Type</td><tr>
    <tr ng-repeat="file in fileArray">
      <td>{{file.name}}</td>
      <td>{{file.lastModified | date  : 'MMMdd,yyyy'}}</td>
      <td>{{file.size}}</td>
      <td>{{file.type}}</td>
    </tr>
    </table></code>
    
  </body>


РЕКОМЕНДУЕТСЯ: Публикация двоичных файлов напрямую

Публикация двоичных файлов с multi-part/form-data неэффективна, так как кодировка base64 добавляет дополнительные 33 % накладных расходов. Если серверный API принимает POST-запросы с двоичными данными, отправьте файл напрямую.

См. Как отправлять двоичные файлы с помощью AngularJS (с ДЕМО)

person georgeawg    schedule 20.01.2017
comment
Выглядит неплохо. Но по какой-то причине, как я это делаю, информация о файле не добавляется к объекту FormData, поэтому я попробую ваш способ. Я уже делаю headers: { 'Content-Type': undefined } Это не работает: angular.forEach($files, function (value, key) { formdata.append(key, value); }); Как вы получили информацию о файле в $scope.files? - person SpaceNinja; 20.01.2017
comment
@georgeawg, не могли бы вы объяснить, как получить данные файла в контроллере laravel. - person Kannan K; 18.04.2018
comment
Когда выбран только один файл, цикл for (var i in $scope.files) {} также перекрывает свойство длины FileList. Вместо этого я использовал for (var i = 0; i < $scope.files.lenght; i++) {}. for/in, использованный выше, будет перебирать все свойства FileList, даже если это не файл w3schools.com/jsref /jsref_forin.asp - person ngige.mm; 11.07.2021