В статье “Частичное скачивание web-странички с помощью Synapse” я рассказывал о том, как можно использовать событие сокета OnReadFilter для фильтрации трафика и частичного скачивания странички сайта. Способ достаточно прост, но наравне с простотой имеется также и ряд недостатков о которых я упомянул в конце статьи. Сегодня рассмотрим способы частичного скачивания файлов любого типа. Вы множество раз сталкивались с таким способом скачивания, используя тот же ReGet – когда весь объем файла разбивается на несколько секций и в многопоточном режиме выкачивается с сервера.
У THTTPSend в Synapse есть два свойства в которых можно задавать начало и окончание диапазона данных, которые необходимо получить от сервера, однако есть несколько моментов, которые следует рассмотреть прежде, чем начинать активно использовать байтовые диапазоны в Synapse.
Общие сведения о байтовых диапазонах
HTTP позволяет запросить не всё содержимое ресурса сразу, а только указанный фрагмент. В таком случае речь идёт о частичных GET, возможность их выполнения необязательна для серверов. Частичные GET в основном, как я уже упомянул, используются для докачки файлов и/или быстрого параллельного скачивания в нескольких потоках.
Для того, чтобы запросить с сервера определенный фрагмент ресурса необходимо определить в запросе заголовок Range (диапазон). В этом заголовке мы должны указать: название единицы измерения (bytes – байты), начало и окончание диапазона. При задании заголовка возможно указывать несколько диапазонов через запятую или опускать одну из частей диапазона. Диапазон указывается через символ “-”.
Рассмотрим пример задания диапазонов. Пусть, например, ресурс X имеет размер в 6000 байт. Вот как можно задавать байтовые диапазоны:
1. Произвольный фрагмент:
Range: bytes=3000-5900
Скачиваем с 3000 по 5900 байт включительно.
2. Один байт:
Range: bytes=3051-3051
Скачиваем 3051 байт документа.
3. Фрагмент от начала до определенного байта:
Range: –5000
Скачиваем с 0 по 5000 байт. Эта запись эквивалентна записи для скачивания произвольного фрагмента, т.е. мы могли бы указать диапазон так:
Range: bytes=0-5000
4. Фрагмент, содержащий количество скачиваемых бай от конца ресурса:
Range: bytes=1000-
Скачиваем последние 1000 байт, т.е. от 5000 до 5999
5. Несколько фрагментов
Range: bytes=1000-, 100-1000
Скачиваем последние 1000 байт, а также фрагмент с 100 по 1000 байт включительно.
Масштаб на рисунках хромает, но. думаю, смысл улавливается нормально.
Как сказано выше – докачка не обязательно должна поддерживаться сервером. Конечно, большинство серверов её поддерживают, но не все и не всегда. Как понять, что сервер поддерживает докачку файлов?
Провести такую проверку можно опытным путем, отправив, например, такой заголовок на сервер:
Range: bytes=1000-
В этом случае ответ сервера может содержать следующие коды статуса:
- 200 (Ok)– докачка файлов не поддерживается, а тело запроса будет содержать весь документ
- 206 (Partial Content) – докачка поддерживается, а тело запроса содержит данные из указанного диапазона
- 416 (Requested Range Not Satisfiable) – ни один из указанных в заголовке Range диапазонов не является корректным.
Теперь посмотрим как реализуется работа с байтовыми диапазонами в Synapse.
Работа с байтовыми диапазонами в Synapse
Для примера рассмотрим закачку какого-нибудь файла с моего блога. Пусть это будет вот файл, расположенный вот по такому URL:
http://webdelphi.ru/wp-content/uploads/downloads/2010/07/Ribbon-Controls-РІ-Delphi-20101.pdf
Для управления диапазонами у THTTPSend имеются два свойства:
property RangeStart: integer read FRangeStart Write FRangeStart; property RangeEnd: integer read FRangeEnd Write FRangeEnd;
Первое свойство задает начало диапазона, а второе — его окончание. Посмотрим, какие из пяти приведенных выше вариантов указания заголовка Range мы сможем воспроизвести в Synapse, манипулирую только этими свойствами.
Создадим простенькое приложение, содержащее 3 Edit’а для задания URL и диапазона и кнопку “Скачать”, а также элементы для вывода служебной информации:
Теперь напишем обработчик OnClick кнопки “Скачать”:
procedure TForm6.Button1Click(Sender: TObject); var HTTP: THTTPSend; begin HTTP:=THTTPSend.Create; try HTTP.RangeStart:=StrToInt(edStart.Text); HTTP.RangeEnd:=StrToInt(edEnd.Text); if HTTP.HTTPMethod('GET',edURL.Text) then begin lbSize.Caption:=IntToStr(HTTP.Document.Size); memHeaders.Clear; memHeaders.Lines.Assign(HTTP.Headers); end else ShowMessage(HTTP.ResultString); finally HTTP.Free; end; end;
Теперь попробуйте изменять различным образом значения начала и окончания диапазона и скачать файл с сервера. При этом Вы можете заметить 2 момента:
- Synapse может реализовать “на автомате” только 1, 2 и 4 ситуацию работы с байтовыми диапазонами. В принципе этого хватит, чтобы организовать многоопоточную “качалку”. Если нет – никто не запрещает нам вручную вписывать заголовок Range в список Headers
- Если задается диапазон 0-10000, то в итоге приходит 10001 байт. Важная мелочь, которая может стоить работоспособности программы
И в заключение, для того, чтобы гарантированно верно “склеивать” отдельно запрошенные фрагменты в один файл необходимо всегда анализировать заголовки Content-Length и Content-Range, возвращаемые сервером. Что касается анализа текстовой части сайта, то о чем я говорил в статье “Частичное скачивание web-странички с помощью Synapse”, то после подробного изучения работы с байтовыми диапазонами я также остался при своем мнении, т.е. использование заголовка Range с целью, например, провести seo анализ сайта приведет к тому, что страница будет качаться целиком с кодом статуса 200. Можете самостоятельно это проверить, например, на моем блоге – сначала скачайте какой-нибудь файл (можно воспользоваться ссылкой выше), а потом попробуйте, используя Range скачать этот пост. Увидите, что во втором случае частичное скачивание не пройдет. Так что для частичного скачивания страниц лучший вариант – это использовать событие OnReadFilter и “мягкая” остановка сокета, через его свойство StopFlag.
Книжная полка
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
||
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|
Что касается анализа текстовой части сайта, то о чем я говорил в статье “Частичное скачивание web-странички с помощью Synapse”, то после подробного изучения работы с байтовыми диапазонами я также остался при своем мнении, т.е. использование заголовка Range с целью, например, провести seo анализ сайта приведет к тому, что страница будет качаться целиком с кодом статуса 200. Не верно, всё зависит от того разрешено ли скачивать частично страницу (разрешает ли сервер), к примеру http://www.wikipedia.org/ тут спокойно можно «скачать» любое количество байт от начала. Вот Ваш сервер видимо не поддерживает частичного скачивания и при этом не сообщает заголовком Accept-Ranges: none что он… Подробнее »
Спасибо за статью. А не подскажите как потом склеить? Есть готовая функция или просто методом «открыть файл первый» и «открыть файл второй» из второго дописать содержимое в первый. Или можно как то по проще?
Brunnen Gi, ну я бы вообще файлы не использовал. Берем два TStream’а — один буфер для приема данных, второй — поток для готового файла и по мере получения информации копируем из буфера данные в поток для будущего файла. В конце операции сохраняем поток в файлик на диске. По-моему все достаточно просто :)
Vlad, конечно просто когда уже поварился в этой каше)) Я просто до этого пытался сделать скачивание на WinInet, но там установка позиции места с которого качать не пашет. На одном из сайтов посоветовали Синапс заюзать для этих целей. К тому же он еще и ранжирование поддерживает адекватно)) И честно говоря я только сегодня с утра начал примеры на синапсе разбирать!)))) Так что необессудь за мои глупые вопросы ;)) Скажи, мне тогда, как правильно сделать многопоточность? Вот например я захотел в два потока качать. Перенес код в поток. И запустил их с разными диапазонами. А что если не весь диапазон скачался?… Подробнее »
На данный момент времени самый волнующий вопрос это:
Как узнать сколько байт уже закачено после запуска функции HTTP.HTTPMethod(‘GET’, URL), если учесть что запущена она была в потоке?
Ммм… ну вот как я сделал.
Я событие на OnStatus привязал к потоку. Т.е. в теории каждый поток будет иметь свой ОнСтатус. Через Синхронизацию можно в принципе его и передавать на форму.
Такой способ правильный?
Brunnen Gi, не ручаюсь, что способ реализован 100% правильно (код не видно), но такой вариант, думаю, имеет право на существование :)
Vlad, а можно будет, когда я допишу программу до чего более-менее видимого, я скину вам исходники, что бы Вы, своим опытным взглядом, посмотрели как все реализовано, правильно ли, ест ли недочеты и какие бы моменты вы сделали по другому? Это возможно?