При составлении АПИ руководствовался принципом — как можно проще. Поэтому сначала несколько мыслей о загрузке файлов:
Загрузка только методом POST. Как показала практика, сам по себе файл почти никогда не бывает отдельной сущностью и всегда привязан к каким-то другим данным в базе, поэтому создавать запись вместе с загрузкой файла — плохая идея. Правильно: сначала создать запись, потом отдельным запросом добавить туда файл. Такой подход снимает кучу вопросов, связанных с отменой загрузки, параллельным редактированием описания файла и проч. Так же, создавая запись предварительно, мы можем получить в ответ данные о количестве свободного места на сервере и использовать их для валидации на клиенте.
Загрузка каждого файла отдельным запросом. На любом хостинге существует ограничение на максимальный размер POST-запроса (напр., 10 МБ). Если мы грузим одновременно 10 файлов, значит их вес в сумме не должен превышать 10МБ. В 99% случаев проще пожертвовать производительностью и не иметь проблем с такими ограничениями.
Никаких отложенных загрузок. Файл должен загружаться сразу после добавления (не в 2000 году, чай, живем), поэтому никаких методов работы с очередью — выбрал файл, выбрал еще 5 штук, удалил один, нажал «отправить» — не будет. Зато будет отмена загрузки.
Не стоит разделять загрузку файлов по кнопке и перетаскиванием. В моем случае любая область, помеченная директивой позволяет перетаскивать на нее файлы, а если это инпут с типом file, то еще и в проводнике можно выбрать. Очень удобно знать, что на кнопку можно перетаскивать, когда потянул в браузер 10 файлов, а дизайнер забыл подсветить поле перетаскивания, и гадаешь перетащатся ли они или откроются в новом окне.
Так же, идеальным, был бы вариант загрузки через метод $save ресурса или подобный, но такого расширения можно долго ждать от разработчиков (костыль на эту тему). Пока так глубоко не влезал, поэтому сделал чуть более топорно.
Особенности модуля
- Загрузка файлов через xhr и iframe (для старых браузеров)
- Чтение изображений до загрузки на сервер (если браузер поддерживает FileReader)
- Одна директива для кнопки и области перетаскивания, как было сказано выше.
- Встроенный валидатор, который при желании можно заменить своим.
- Встроенная функция обработки ошибок, которую так же можно заменить своей.
Планы на будущее (с вашей помощью 😉
- Общий прогресс для одновременно загружаемых файлов.
- Ограничение количества одновременно загружаемых фалов
- Уменьшение картинки на клиенте
- Отмена загрузки (xhr.abort() и т.п.).
- Допиливание загрузчиков для старых браузеров.
Подключение
Подключение модуля:
angular.module('myApp', ['oi.file']);
Использование директивы в HTML-шаблоне:
<!-- Загрузка через проводник и перетаскиванием --> <input type="file" oi-file="options"> <!-- Загрузка перетаскиванием на область --> <ul oi-file="options"> <li ng-repeat="item in items"> <img ng-src="{{item.thumb}}"> </li> </ul>
Настройка в контроллере:
$scope.file = {} //Модель $scope.options = { //Вызывается для каждого выбранного файла change: function (file) { //В file содержится информация о файле //Загружаем на сервер file.$upload('uploader.php', $scope.file) }) } }
Создание элемента модели для каждого файла:
$scope.items = model; $scope.options = { change: function (file) { //Создаем пустой элемент для будущего файла $scope.add(function (i, id) { //Загружаем картинку через FileReader до загрузки на сервер file.$preview($scope.items[i]); //Загружаем на сервер file.$upload('uploader.php' + id, $scope.items[i], {allowedType: ["jpeg", "jpg", "png"]}) .catch(function (data) { //Удаляем элемент при неудачной загрузке $scope.del(data.item.id); }) }) } }
Метод catch
доступен, начиная с Ангуляра 1.2. В старых версиях используйте вместо него then(null, function (data) {...})
. $preview
и $upload
возвращают обещания. См. $q.
Пример с уменьшением изображения на клиенте:
file.$preview({}) .then(function (data) { //Изображение прочитано. Уменьшаем его с помощью canvas minimize(file._file); //Отправляем file.$upload('uploader.php', $scope.avatar) }, function (data) { //Изображение не прочитано. Отправляем как есть file.$upload('uploader.php', $scope.avatar) });
Исходники, демо
Гитхаб, демонстрация, песочница
Аналоги
Параллельно nervgh написал angular-file-upload с гораздо более богатым АПИ, где на каждый чих формируется событие. Обратите внимание, кого не устроит бедность моего.
ссылка на оригинал статьи http://habrahabr.ru/post/191464/
Добавить комментарий