Я предполагаю, что у вас есть некоторые базовые знания об Angular и TypeScript и немного о ASP.NET Core. Если нет, я настоятельно рекомендую погрузиться в официальный учебник, который охватывает все, чтобы понять все, что ниже.

Вы также найдете здесь полный код:
https://github.com/bjr001/angular-file-uploading-example

Итак, у вашего клиента есть это - конечно, гибкое - требование, которое может выглядеть так:

«Как пользователь я хочу загрузить несколько файлов, не замораживая пользовательский интерфейс».

Кусок торта, не так ли? Зайдите в Google, выберите одну из тех, что кажется, 10000 библиотек, внедрите ее, и работа сделана.

Да, хорошая попытка…. работая с этой модной сторонней библиотекой, вы чувствуете, что работа может быть выполнена не так быстро. Вам нужно немного подправить здесь и там, (в основном) несколько раз взглянуть на документацию API и / или пройти через бесконечные ответы на stackoverflow.

В конце концов вы можете спросить себя, действительно ли это так сложно сделать самостоятельно? Нет, это не так! Я могу сказать тебе. На самом деле я могу вам показать!

Прежде всего, давайте подробнее рассмотрим функциональные требования:

  • Поставить в очередь несколько файлов
  • Есть только одна активная загрузка
  • Не замораживайте интерфейс
  • Получайте прогресс при каждой загрузке

Я не буду вдаваться в нефункциональные требования, например, чтобы обеспечить удобство работы с пользователем, и не буду показывать вам, как реализовать индикатор выполнения - по крайней мере, не в рамках этой истории;)

Давай прекратим тарабарщину и займемся клавиатурой. Мы начнем с более простого - бэкэнд и перейдем к фронтенду.

Бэкэнд

Сделайте свой контроллер способным получать файлы с помощью следующего кода:

В IFormFile мы используем встроенный связыватель модели для легкого доступа к информации, такой как тип содержимого, длина, имя, имя файла, а также получаем очень удобный метод для передачи самого файла в поток - спасибо команде ASP.NET Core!

См. Также здесь:
https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-2.1#uploading-small-files-with-model -обвязка

Внешний интерфейс

Что касается внешнего интерфейса, нам нужно сделать еще немного. Чтобы вы лучше понимали общую картину, я покажу вам упрощенную диаграмму классов, что нам нужно для выполнения работы:

Вместо того, чтобы помещать все наши файлы в корень нашей папки приложения, я просто создам отдельный модуль:

ng g m file-upload

В результате будет получен следующий код - не запутайтесь в UploadComponent (получите их в ближайшее время):

Теперь мы собираемся создать перечисление, чтобы указать статус процесса загрузки:

ng g e file-upload/upload-status

Затем мы создадим класс, который фактически представляет сам загружаемый файл. Три метода complete (), failed () и updateProgress () будут отвечать за управление состоянием, которое, в свою очередь, является текущим статусом и прогресс:

ng g class file-upload/file-upload

Пока все хорошо, верно! Пора перейти к главному. UploadService будет отвечать за поддержание очереди файлов, которые мы хотим загрузить. Он также создает запрос на загрузку файлов, который затем будет выполнен Angular HttpClient.

Чтобы отслеживать файлы в очереди и их текущий прогресс, мы будем использовать RxJS (который уже поставляется с Angular, поскольку он сильно развит).

Но как этого добиться? Как я упоминал ранее, мы создадим только правильный HTTPRequest типа FormData, который затем передадим в запрос метода HttpClient, встроенный в Angular. Взглянув на описание этого метода, мы увидим следующее:

* Отправить заданный `HttpRequest` и вернуть поток` HttpEvents`.

Подписавшись на эту наблюдаемую, мы получим уведомление о новых HttpEventTypes, где мы сможем вывести текущий статус и процент активного прогресса загрузки. Это именно то, что нам нужно!

Другая сложная часть - это удержание очереди, чего мы добьемся с помощью BehaviorSubject, который мы будем использовать из-за некоторых особенностей по сравнению с Subject. Самая важная особенность для нас - это то, что у него есть какое-то состояние. Это означает, что он всегда возвращает значение, когда потребитель подписывается на него, чего не произойдет с Subject и / или Observable. Конечно, это подразумевает наличие начального значения при создании BehaviorSubject.

Почему нам это надо? Что ж ... реализуя это поведение, мы можем гарантировать, что наш потребитель / компонент может подписаться на очередь. Кроме того, мы в любое время сможем увидеть прогресс каждого файла, помещенного в очередь.

Для получения дополнительной информации см. Http://reactivex.io/rxjs/manual/overview.html#behaviorsubject, а также здесь https://stackoverflow.com/questions/39494058/behaviorsubject-vs-observable, что дало мне лучшее непонятный.

Не забудьте зарегистрировать службу в своем модуле (FileUploadModule) или использовать новый декоратор providedIn: root, что мы и сделаем;)

ng g service file-upload/upload

Если вы все еще со мной, мы почти закончили! И последнее, но не менее важное: нам нужен какой-то компонент, который потребляет нашу недавно созданную службу. А пока давайте будем простыми и будем использовать простую кнопку для загрузки локальных файлов.

(Я покажу вам в следующей статье, как отслеживать прогресс и составлять список загружаемых файлов в очереди)

ng g c file-upload/upload

И вот оно: никакого черного ящика, никакой магии, никаких дополнительных зависимостей от сторонних библиотек!

Некоторые подводные камни, с которыми я боролся: