![]() |
|
|
|||||||
| Регистрация | << Правила форума >> | 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
|
||||
|
||||
|
Цитата:
|
|
#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; Может быть у кого будут какие идеи по-поводу прогресса? |