|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
||||
|
||||
Скачивание файла в несколько потоков
Пытаюсь организовать загрузку файла (одного файла) в несколько потоков, как, например, в Free Download Manager. Поиски в инете привели к следующему результату:
Код:
type TDLoad = class(TThread) private Strim: TMemoryStream; {вариант: Strim: TFileStream; - тут пока не ясно} URL: String; S, E: Cardinal; //Начальный и конечный байт для скачивания protected procedure Execute; override; public end; { TDLoad } procedure TDLoad.Execute; var h: TIdHTTP; Strim: TMemoryStream; begin inherited Execute; h:=TIdHTTP.Create(nil); h.HandleRedirects:=True; //задаем h.Request.UserAgent:= //нужные h.Request.Referer:= //параметры запроса h.OnWork:= //здесь можно визуализировать h.OnWorkBegin:= //процесс с помощью ProgressBar h.Request.ContentRangeStart:= S; //откуда начинаем закачивать h.Request.ContentRangeEnd:= E; //где заканчиваем Strim:= TMemoryStream.Create; //либо это нужно делать далее, в цикле //и передавать потоку уже созданный TMemoryStream ............................ try h.Get(URL,Strim); //если все удачно, то в Strim мы имеем кусок нужного файла Except on E : Exception do ShowMessage('Нету'+#13#10+IntToStr(h.ResponseCode)); end; h.Free; end; запрашиваем размер, делим на части, Код:
procedure TForm1.Button1Click(Sender: TObject); var i: Byte; dl: TDLoad; ms: TMemoryStream //Как вариант begin ................................. for i:= 0 to {кол-во частей} do begin .................................. ms:= TMemoryStream.Create;//вариан dl:= TDLoad.Create(True); dl.S:= //задаем начало dl.E:= //и конец части dl.Strim:= ms; end; ................................... //теперь нужно отловить завершение всех dl: TDLoad //и собрать все потоки в один файл очевидно понадобится TFileStream end; а вот как собрать части в один файл не ясно или есть какая то возможность записывать части сразу в FileStream? |
#2
|
|||
|
|||
1. Отлов завершения потоков. Вариантов много. Например, т.к. ты заранее знаешь кол-во потоков (блоков файла), то можно создать массив, ссылку на который передавать потокам. Каждый поток там проставит флажек, что завершился. А главный поток просто проверяет массивы состояний и соотв. обновляет глобальную информацию.
2. Т.к. ты знаешь размер файла до начала его скачивания, то сначала создай соотв файл (читай - TFileStream) и ссылку на него передавай каждому потоку вместе с началом и концом. Каждый поток скачает данные во времененый буфер (TMemorystream), а потом просто запишет его в нужное место файла. Не забудь синхронизовать потоки в момент записи через CriticalSection. 3. Писать сразу в результирующий поток не получится, т.к. компоненты этого не умеют. Хотя, если подправишь сами компоненты, то можно и без промежуточного буфера обойтись. |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Помидоркин (04.05.2015)
|
#3
|
||||
|
||||
Цитата:
Код:
procedure TForm1.Button1Click(Sender: TObject); var i: Byte; TDLoad; begin ................................. for i:= 0 to {кол-во частей} do begin .................................. dl:= TDLoad.Create(True); ..................................... end; ................................... while {не все потоки поставили флажок (завершились)} do Sleep(1000); end; Код:
var Form1: TForm1; TrCnt: Byte; CntLock: TCriticalSection; implementation Код:
procedure TForm1.Button1Click(Sender: TObject); var i: Byte; dl: TDLoad; begin ................................. for i:= 0 to {кол-во частей} do begin .................................. CntLock.Enter; Inc(TrCnt); CntLock.Leave; end; ................................... while TrCnt>0 do Sleep(1000); end; Код:
procedure TDLoad.Execute; var h: TIdHTTP; Strim: TMemoryStream; begin ............................ h.Get(URL,Strim); //если все удачно, то в Strim мы имеем кусок нужного файла ............................... h.Free; CntLock.Enter; Dec(TrCnt); CntLock.Leave; end; Цитата:
Код:
type TDLoad = class(TThread) private Strim: TFileStream; .............................. protected procedure Execute; override; end; { TDLoad } procedure TDLoad.Execute; var h: TIdHTTP; Buf: TMemoryStream; begin ................................ Buf:= TMemoryStream.Create; ............................ try h.Get(URL,Strim); Except on E : Exception do ShowMessage('Нету'+#13#10+IntToStr(h.ResponseCode)); end; h.Free; CntLock.Enter; Strim.Position:=S; //не уверен Strim.CopyFrom(Buf,Buf.Size); //не уверен CntLock.Leave; Buf.Free; end; Код:
procedure TForm1.Button2Click(Sender: TObject); var i: Byte; dl: TDLoad; fs: TFileStream; begin fs:= TFileStream.Create('D:\cashe\001.mp4',fmCreate); fs.Size:={.Response.ContentLength}; for i:= 0 to {кол-во частей} do begin dl:= TDLoad.Create(True); dl.S:= //Начало dl.E:= //Конец dl.Strim:= fs; //здесь не уверен dl.Resume; end; end; Цитата:
|
#4
|
|||
|
|||
1. А что мешает завести еще один поток, который будет заниматься этим? Один на все скачки, если их много.
2. Еще раз. В момент настройки скачки ты получаешь размер всего файла. соответственно, можешь аллоцировать место (записать нули в TFileStream в нужном кол-ве). часть файла, скачанная в TMemoryStream - ты всегда знаешь с какой позиции надо писать, а длинна - размер TMemoryStream. |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Помидоркин (05.05.2015)
|
#5
|
||||
|
||||
Цитата:
Цитата:
Код:
fs.Size:={.Response.ContentLength}; Цитата:
Код:
CntLock.Enter; Strim.Position:=S; Strim.CopyFrom(Buf,Buf.Size); CntLock.Leave; Все-таки смущает: Код:
type TDLoad = class(TThread) private Strim: TFileStream; .............................. Код:
procedure TForm1.Button2Click(Sender: TObject); ................... begin fs:= TFileStream.Create('D:\cashe\001.mp4',fmCreate); ................... for i:= 0 to {кол-во частей} do begin dl:= TDLoad.Create(True); ..................... dl.Strim:= fs;. end; end; |
#6
|
||||
|
||||
Цитата:
|
#7
|
||||
|
||||
Цитата:
— Как тебя понимать? — Понимать меня не обязательно. Обязательно меня любить и кормить вовремя. На Delphi, увы, больше не программирую. Рекомендуемая литература по программированию |
#8
|
||||
|
||||
Цитата:
|
#9
|
||||
|
||||
Вот, собственно, что получилось в итоге:
Код:
type TDLoad = class(TThread) private URL: String; S, E: Cardinal; Buf: TMemoryStream; procedure WriteStrim; protected procedure Execute; override; public end; { TDLoad } procedure TDLoad.Execute; var h: TIdHTTP; begin h:=TIdHTTP.Create(nil); {настраиваю запрос} h.OnWork:= Form1.IdHTTPWork; h.Request.ContentRangeStart:= S; h.Request.ContentRangeEnd:= E; Buf:= TMemoryStream.Create; try h.Get(URL,Buf); Except on E : Exception do //тут надо-бы предусмотреть обработку ошибки end; h.Free; Buf.Position:=0; Synchronize(WriteStrim); Buf.Free; CntLock.Enter; Dec(TrCnt); CntLock.Leave; // TrCnt: Byte - счетчик потоков, CntLock: TCriticalSection; end; procedure TDLoad.WriteStrim; begin FileStrim.Position:=S; FileStrim.CopyFrom(Buf,Buf.Size); end; Код:
procedure TForm1.FormCreate(Sender: TObject); begin CntLock:=TCriticalSection.Create; Link:= 'http://dedowsk-beauty.ru/0/Gloria_Gaynor__I_Will_Survive.mp3'; end; procedure TForm1.Button2Click(Sender: TObject); var i: Byte; dl: TDLoad; h: TIdHTTP; CntntLen: Int64; begin h:=TIdHTTP.Create(nil); {настраиваю запрос} try h.Head(Link); Except on E : Exception do begin ShowMessage('Нету'+#13#10+IntToStr(h.ResponseCode)); Exit; end; end; CntntLen:=h.Response.ContentLength; h.Free; FileStrim:= TFileStream.Create('D:\cashe\Gloria_Gaynor__I_Will_Survive.mp3',fmCreate); FileStrim.Size:=CntntLen; PrBar.Max:= SecLen; // тут пришлось приделывать "костыли" PrBar.Position:=0; TrCnt:=0; for i:= 0 to CntntLen div SecLen do //const SecLen: Integer = 2097152; (2MB) begin dl:= TDLoad.Create(True); dl.FreeOnTerminate:=True; dl.URL:= Link; dl.S:= i*SecLen; dl.E:= (i+1)*SecLen-1; dl.Resume; CntLock.Enter; Inc(TrCnt); CntLock.Leave; end; while TrCnt>0 do begin Label1.Caption:=IntToStr(TrCnt); Sleep(1000); end; Label1.Caption:=IntToStr(TrCnt); PrBar.Position:=0; FileStrim.Free; end; Код:
procedure TForm1.IdHTTPWork(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer); begin PrBar.Position:= AWorkCount; end; Может быть у кого будут какие идеи по-поводу прогресса? |
#10
|
||||
|
||||
Может так
Код:
if PrBar.Position < AWorkCount then PrBar.Position:= AWorkCount; Я не понял Вашего вопроса, но всё же Вам на него отвечу! |
Этот пользователь сказал Спасибо Alegun за это полезное сообщение: | ||
Помидоркин (08.05.2015)
|
#11
|
||||
|
||||
Цитата:
Вижу только два варианта: 1. наследовать от IdHTTP, добавить поле типа Int64 и запоминать в него предыдущее значение AWorkCount 2. ProgressBar для каждого потока, но уж больно громоздко. Теперь пытаюсь все это безобразие прикрутить к динамической форме, пока не очень-то получается |