09-08-2022

Обработка файлов из Flutter в PHP

Обработка файлов из Flutter в PHP
NJ Soft

Данная задача часто встречается при разработке мобильного приложения.

Например: требуется создать мобильное приложение, которое будет принимать файлы от пользователя. Для упрощения объяснения, предположим что в приложении будет только «поле для выбора файла» и «кнопка отправить» - максимально простое приложение.

А на сервере будет PHP-код, который обрабатывает входящий HTTP-запрос из Flutter приложения.

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

Align(
alignment: Alignment.bottomRight,
child: IconButton
onPressed: () {
filePick();
},
icon: const ImageIcon(AssetImage('images/icon_attachment.png'))
),
)

Где функция filePick реализована через библиотеку file_picker:

void filePick() async {
/// https://pub.dev/packages/file_picker
FilePickerResult? result = await FilePicker.platform.pickFiles();

if (result != null
&& result!.files != null
&& result!.files!.single != null
&& result!.files!.single!.path != null
) {
File file = File(result!.files!.single!.path!);
String fileName = file.path.split('/').last;

setState(() {
_fileName = fileName;
_file = file;
});

} else {
// User canceled the picker
}
}

При вызове filePick() в Android или iOS → откроется встроенный (стандартный) менеджер выбора файлов. После того как файл выбран на основе его расположения создаётся объект File ( из dart::io) и теперь при нажатии на кнопку отправить вызываем HTTP-запрос в который подставим данные из File (_file) в виде Uint8list.

Код HTTP-запроса:

final response = await http.post(
Uri.parse('/api/send/file/'),
headers: {
'Content-Type': 'application/json',
'Host': 'njsoft.dev'
},
body: jsonEncode( {
'flutterUnsigned8integers': _file.readAsBytesSync(),
'file_name': _fileName
})
)

Метод readAsBytesSync возвращает тип Uint8List. Это массив фиксированной длинны из 8-битных целых чисел без знака. Результатом jsonEncode будет json-строка.

Пример:

{
"flutterUnsigned8integers":[37,80,68,70,45,49,46,52,10,37,211,235,233,225,10...],
"file_name": "выбранный файл.pdf"
}

В flutterUnsigned8Integers будет находится массив из чисел. Это будет представление файла в числах. Каждое число это 8-бит, т.е это байт. Следовательно на сервере надо будет выполнить запись байтов в файл.

В PHP где происходит обработка входящего HTTP-запроса, делаем код:

// Обработка запрос POST /api/send/file

// Парсинг json
$dataFromFlutter=json_decode(file_get_contents('php://input'),true);
// Создаём ресурс на входящий файл в бинарном режиме на запись
$file=fopen("/path/to/file/".basename($dataFromFlutter['file_name']), "w");
// Читаем данные файла — это числа от 0 до 255
foreach ($dataFromFlutter['flutterUnsigned8integers'] as $integer) {
// Записываем символ в бинарном представлении (8 бит → в байт)
fwrite($file, pack('C', $integer));
}

fclose($file);
// Теперь можно работать с файлом

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

Поэтому применяем упаковку числа в бинарное представление с помощью pack и с помощью формата «C» - беззнаковый символ. Чтобы понять почему выбран беззнаковый символ обратимся к документации по PHP для типа строки.

Обработка файлов из Flutter в PHP - иллюстрация 1

И видим что в PHP строка представлена символами, а 1 символ = 1 байту.

dev mobile flutter php