I'm trying to implement a multi-threaded download file procedure using TIdHTTP
.
To achieve this, I'm using the TIdHTTP.Request.Range
property to set the part of the file that will be downloaded by each thread. The download procedure is working, but I'm stuck because I don't know how to join the parts into a single file (and not corrupt it).
Below is my code. In this example, I'm downloading the file from the same server using 2 threads, but the idea is to download the same file from different servers, to increase overall download speed. When the threads end, I have two files, each with half the size of the original file. How do I transform these two files into one file?
unit princ;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, IdIOHandler, IdIOHandlerSocket,
IdIOHandlerStack, IdSSL, IdSSLOpenSSL, Vcl.ComCtrls;
type
TDLThread = class(TThread)
private
procedure work(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
public
var
remoteFile, destination: string;
currentPart, totalParts: integer;
protected
procedure Execute; override;
end;
type
TForm1 = class(TForm)
Button1: TButton;
IdHTTP1: TIdHTTP;
IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;
ProgressBar1: TProgressBar;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
var
processes: integer;
var
dlthread: TDLThread;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
i, totalParts: integer;
begin
totalParts := 2;
for i := 1 to totalParts do
begin
dlthread := TDLThread.Create(True);
dlthread.remoteFile := 'https://download.jgsoft.com/editpad/SetupEditPadLite.exe';
dlthread.destination := 'c:\users\admin\desktop\';
dlthread.totalParts := totalParts;
dlthread.currentPart := i;
dlthread.Resume;
end;
end;
{ TDLThread }
procedure TDLThread.Execute;
var
idh: TIdHTTP;
ssl: TIdSSLIOHandlerSocketOpenSSL;
f: TFileStream;
remoteFileName, localFileName : string;
i, fileSize, rangeLength, rangeStart, rangeEnd: integer;
begin
inherited;
for i := 1 to length(remoteFile) do
begin
if remoteFile[i] = '/' then
remoteFileName := ''
else
remoteFileName := remoteFileName + remoteFile[i];
end;
idh := TIdHTTP.Create(Form1);
idh.OnWork := work;
ssl := TIdSSLIOHandlerSocketOpenSSL.create(Form1);
ssl.SSLOptions.Method := sslvSSLv23;
idh.IOHandler := ssl;
localFileName := remoteFileName + '_' + IntToStr(currentPart);
f := TFileStream.Create(destination + localFileName, fmCreate);
idh.Head(remoteFile);
fileSize := idh.Response.ContentLength;
rangeLength := fileSize div totalParts;
rangeStart := 1 + (rangeLength * (currentPart-1));
rangeEnd := (rangeLength * currentPart);
idh.Request.Range := 'bytes=' + IntToStr(rangeStart) + '-' + IntToStr(rangeEnd);
idh.Get(remoteFile, f);
idh.Free;
f.Free;
end;
procedure TDLThread.work(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
var
Http: TIdHTTP;
ContentLength: Int64;
Percent: integer;
begin
Http := TIdHTTP(ASender);
ContentLength := Http.Response.ContentLength;
if (Pos('chunked', LowerCase(Http.Response.TransferEncoding)) = 0) and (ContentLength > 0) then
begin
Percent := 100 * AWorkCount div ContentLength;
Form1.ProgressBar1.Position := Percent;
end;
end;
end.