Delphi. Datasnap. Как передать файл (изображение) на сервер, разделив его на части?

Внимание!!! Уже после того, как я опубликовал этот пост – нашел неприятный момент алгоритма – ужасно съедается память. Так что, к сожалению, этот алгоритм не рабочий… 

Оно и понятно, в процедуре Ntransfer, переменная создается внутри цикла for… А удаляется она всего 1 раз.  По другому я заставить работать программу не смог. Тут либо какой-то глюк, либо я чего-то не понимаю, но суть в том, что если создать процедуру на сервере с параметром tstream, то её нельзя использовать многократно, а вот если создать с параметром строки, например, то уже можно. Об этом я напишу в отдельном посте.

В данном посте я расскажу о том как можно передать файл (изображение) с клиента DataSnap на сервер приложений DataSnap, предварительно разделив его на части.

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

Скажу сразу, этот метод у меня более менее хорошо работал на файлах до 3 мб.(от 3 до 6 не проверял)  На 6 мб. система вылетала, цитирую “Недостаточно памяти”… Если нужно обрабатывать большие файлы, нужно усовершенствовать алгоритм…

Чем меньше файл- тем быстрее работает алгоритм. Тут если говорить о чистой отправке и загрузке – то он работает ещё быстрее. Но в предложенной ниже схеме отображение из БД происходит через кэширующие элементы. И пока в них загрузится… для отображения клиенту. Словом, тут есть над чем подумать, например, не таскать туда сюда файлы, а отображать только список из БД…. Но это в другом посте.

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

Что нужно сделать на сервере приложений DataSnap?

Компоненты…

1

 

Какие переменные и методы добавить на сервере приложений DataSnap?

Глобальная переменная… В неё будем собирать из частей файл / изображение…

Процедура обнуления переменной

Процедура записи в базу данных…

Процедура сборки по частям…

 

Что нужно сделать на клиенте?

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

 

Далее, среди глобальных переменных главного клиентского юнита – объявил 2 следующие переменные…

Вспомогательные процедуры…

Для загрузки картинок в память…

Далее, 2 процедуры передачи – одна для целых частей, другая для остатка. Идея в чем – у нас есть изображение, скажем 587*1024 байт. Мы устанавливаем размер пакета, скажем 51200 байт. Делим 587*1024 на 51 200 байт и получаем какое-то целое число пакетов + остаток… Ну и соответственно передаем их…

 

Далее, заполняем процедуру Execute созданного нами класса потока TTransferThread…

Ну и последний шаг – “повесить” на кнопку запуск потока…

Вот результат…
4

 

Что можно улучшить?

-Добавить различные обработки вероятных ошибок…

-Добавить процесс загрузки картинки в отдельный поток (Thread), чтобы по этой причине уменьшить вероятность зависания сервера…

-Для файлов, скажем > 3мб… можно усовершенствовать алгоритм – делить исходный файл на части по 3 Мб. + остаток и отсылать их очередями, используя алгоритм данного поста…

Почему может не работать?

Опишу свои случаи, с которыми я сталкивался, работая над данным алгоритмом…

Remote Error Out of Memory…

Возникала у меня тогда, когда я не проставил размер буферной переменной у SQLConnection1 в свойстве Driver. По умолчанию там стоит 32 Кб, а если размер пакета, скажем 50 Кб (50*1024=51200 байт), то если замерить поток на входе в сервер, он будет равен -1, соответственно, остальной алгоритм не будет работать. Итак, что нужно сделать? Выставить переменную BufferKBSize у SQLConnection1 в максимально возможное число = 1000 Кb…

Аналогично в разделе сервера ServerContainer, у DSTCPServerTransport, если зайти в объектный инспектор, можно также встретить эту переменную. Её я тоже выставил в максимальное значение 1000 Kb…

MySQL has gone away…

Это уже более частная проблема, с которой столкнулся лично я. Для её решения, нужно поправить размер max_allowed_packet, можно это сделать из консоли MySQL, дав команду…

Но лучше прописать это изменение в конфигурационном файле, иначе при следующем перезапуске сервера произойдет та же самая ошибка…