Л А Б О Р А Т О Р И Я

актуальных

РЕШЕНИЙ


Flutter > Dart > dArtist13.06.2023

Flutter - отправка файла на сервер, показ на странице картинки загруженной на сервер.

Изложенный материал рассчитан на подготовленных разработчиков, обладающих знаниями ООП (объектно-ориентированного программирования), а также работающих в настроенной среде Flutter и имеющих базовое представление о фреймворке и языке Dart.

Основная задача - осуществить передачу (загрузку) файла картинки выбранной пользователем на сервер, после получения ответа от сервера о загруженном файле отобразить картинку на странице. Для тестирования используется браузер Google Chrome.

Реализация задачи может быть осуществлена с применением различных пакетов и библиотек, облегчающих (вероятно) работу с передачей данных на сервер. Чаще всего для подобных задач используется пакет DIO из библиотеки "dart: io". Но здесь мы попытаемся осуществить передачу данных наиболее простым способом, используя class HttpRequest библиотеки "dart: html". Также не будем обращать сильно внимание на безопасность, так как основная цель - показать простой способ загрузки файла.


Вид единственной страницы, на которой будем осуществлять все необходимые действия:

list screens

После нажатия на кнопку "Выбрать" открывается диалоговое окно выбора файла (показан набор файлов на отдельном пользовательском устройстве):

dialog screen

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

script PHP

Если РНР скриптом будут получены коррректные данные, то файл будет сохранен в папку "userfiles", под тем же именем, которое ему было присвоено на устройстве пользователя. Затем сервер отправит назад в формате JSON массив параметров файла из $_FILES, название файла из $_POST, время и title.


Полученные данные от сервера можно будет посмотреть в терминале, они будут иметь подобный вид:

data terminal

После получения данных от сервера обновится состояние виджета страницы и мы покажем на ней картинку загруженную на сервер:

img view

Есть один нюанс о котором стоит упомянуть. При тестировании на локальном компьютере из-за политики CORS могут быть недоступны файлы на удаленном сервере. И это может привести не к показу загруженной на сервер картинки, а к выводу вместо нее подобного сообщения на странице:

CORS img view

Решение этой проблемы было найдено на ресурсе stackoverflow. Там было предложено запускать flutter для тестирования следующей командой:

flutter command

Стоит отметить, что применять эту команду надо будет при каждом запуске flutter, когда будет необходимо получать данные по ссылке на удаленный сервер. Соответственно и на сервере должны быть сняты ограничания доступа.


Теперь перейдем непосредственно к коду. Для удобства, крупные файлы буду рассматривать по частям.


main.dart:

code screen

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


upload_screen.dart часть 1:

code screen

Рассмотрим наиболее значимые строки.

строка №2 (file: upload_screen.dart)

import 'dart:html' as html_d;

Подключаем библиотеку в которой имеется класс HttpRequest, с помощью которого будем осуществлять отправку данных на сервер. Обращаться к библиотеке будем по алиасу (псевдониму) html_d, что будет проще и не будем путаться в аббревиатурах. На скрине строка подчеркнута - это предупреждение от VS Code о том, что данная библиотека используется только в WEB.


строка №3 (file: upload_screen.dart)

import 'dart:convert';

Подключаем библиотеку для обработки данных в формате JSON.


строка №5 (file: upload_screen.dart)

class UploadScreen extends StatelessWidget {

Объявляем класс виджета, который будет показан на странице.


строка №14 (file: upload_screen.dart)

child: UploadFormField(),

Объявляем дочерний виджет, который будет размещен по центру виджета страницы.


строка №19 (file: upload_screen.dart)

class UploadFormField extends StatefulWidget {

Объявляем класс виджета с состоянием, который будет изменяться при отправке данных на сервер и получении от него ответной информации.


строка №22 (file: upload_screen.dart)

State <UploadFormField> createState() => _UploadFormFieldState();

Объявляем виджет состояния.


upload_screen.dart часть 2:

code screen

строка №25 (file: upload_screen.dart)

class _UploadFormFieldState extends State<UploadFormField> {

Объявляем класс виджета состояния.


строка №26 (file: upload_screen.dart)

final String _pathScript = "http://test.a77r.ru/test_upload_file.php";

Определяем защищенную переменную _pathScript, в которой записан адрес скрипта на сервере.


строка №27 (file: upload_screen.dart)

String _imagePath = '';

Определяем защищенную переменную _imagePath, в которой будет записана ссылка на загруженную на сервер картинку. Первоначальное значение переменной - пустая строка.


строка №29 (file: upload_screen.dart)

void updateView(String imgPath) {

Объявляем функцию, которая будет проверять соответствие полученного адреса картинки с сервера (переменная imgPath) со значением текущим в защищенной переменной _imagePath. Если они равны, а это возможно, когда в обеих переменных находятся пустые строки, то прекращаем исполнение функции (см. строка 30). В противном случае, запускаем перерисовку виджета и изменяем значение защищенной переменной _imagePath, присваиваем ей полученную ссылку на картинку (см. строки 31-33).


строка №36 (file: upload_screen.dart)

void loadFile() async {

Определяем асинхронную функцию загрузки файла (см. строки 36-60). В функции будет сформирован объект FormData (см. строки 43-45), который будет отправлен на сервер (см. строка 47). Результатом будет передача адреса картинки загруженной на сервер или пустой строки, в зависимости от полученного ответа, в вызываемую функцию updateView (см. строка 59).


строка №38 (file: upload_screen.dart)

final htmlFile = await pickHtmlFile();

Определяем переменную htmlFile и присваиваем ей значение полученное от вызванной функции pickHtmlFile (см. строки 62-69). Функция асинхронная, поэтому дожидаемся получения ответа от нее. После получения ответа в переменной будет храниться объект выбранного пользователем файла.


строка №41 (file: upload_screen.dart)

if (htmlFile.size > 1000000) return;

Проверка размера выбранного файла, если размер превышает 1 Мб, то прекращаем выполнение. Это сделано для примера, как вариант улучшения безопасности. Могут быть и другие решения для обработки входных данных файла.


строка №43 (file: upload_screen.dart)

final formData = html_d.FormData();

Создаем объект формы и присваиваем переменной formData.


строка №44 (file: upload_screen.dart)

formData.appendBlob('userfile', htmlFile, htmlFile.name);

Добавляем в объект формы данные загруженного файла. В скрипте РНР будем получать в массиве данных $_FILES.


строка №45 (file: upload_screen.dart)

formData.append('namefile', htmlFile.name);

Добавляем в объект формы данные имени файла. В скрипте РНР будем получать в массиве данных $_POST. Это сделано для примера, т.к. имя можно получить из данных предыдущей строки. Основная цель - показать возможность передачи дополнительных данных.


строка №47 (file: upload_screen.dart)

html_d.HttpRequest.request(_pathScript, method: 'POST', sendData: formData)

Отправка объекта данных на сервер методом POST.


строка №48 (file: upload_screen.dart)

.then((html_d.HttpRequest rspns) {

Полученный от сервера ответ присваиваем переменной rspns.


строка №49 (file: upload_screen.dart)

print(rspns.responseText);

Выводим полученный от сервера ответ в консоль терминала.


строка №52 (file: upload_screen.dart)

var result = json.decode(rspns.responseText.toString());

Декодируем полученный ответ от сервера из формата JSON.


строка №53 (file: upload_screen.dart)

imagePath = 'http://test.a77r.ru/userfiles/' + result['file_name_post'];

Формируем адрес ссылки на картинку на основании полученных от сервера данных.


строка №55 (file: upload_screen.dart)

}).catchError((error) {

Блок обработки ошибок. Для примера выводятся в консоль, тип ошибки и в какой фазе появилась ошибка (см. строки 56-57). Возможны варианты обработки ошибок и реагирования на них. Если ошибка произошла, то переменной imagePath присваивается значение пустой строки, что прекратит выполнение в функции updateView (см. строка 30).


строка №59 (file: upload_screen.dart)

}).whenComplete(() => updateView(imagePath));

Запускаем функцию updateView и передаем в нее значение переменной imagePath - путь к картинке на сервере.


строка №62 (file: upload_screen.dart)

Future<html_d.File?> pickHtmlFile() async {

Опеределяем асинхронную функцию pickHtmlFile, которая обрабатывает диалог по выбору файла на устройстве пользователя.


строка №63 (file: upload_screen.dart)

final uploadElement = html_d.FileUploadInputElement()

Создаем объект элемента загрузки файла и присваиваем его переменной uploadElement.


строка №64 (file: upload_screen.dart)

..multiple = false

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


строка №65 (file: upload_screen.dart)

..accept = 'image/*'

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


строка №66 (file: upload_screen.dart)

..click();

Запуск открытия диалогового окна для выбора файла пользователем.


строка №67 (file: upload_screen.dart)

await uploadElement.onChange.first;

Ожидаем изменений в объекте загрузки файла.


строка №68 (file: upload_screen.dart)

return uploadElement.files?.first;

Возвращаем значение из объекта загрузки файла.


Далее (см строки 72-89) достаточно стандартный код для построения виджета. Стоит обратить внимание на отображение картинки, она будет выводиться только когда длина строки адреса будет больше нуля (см. строку 78)


строка №79 (file: upload_screen.dart)

? Image.network(_imagePath)

Если длина строки адреса будет больше нуля, то будет отображаться картинка, адрес которой содержиться в защищенной переменной _imagePath.


Удачи в разработке!


оставить комментарий

НИК или имя:


E-mail:


Комментарий

(проходит предварительную модерацию перед публикацией)



другие материалы: