Delphi-IdHTTP多线程下载

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls, IdComponent, IdTCPConnection, IdTCPClient,
IdHTTP, IdBaseComponent, IdAntiFreezeBase, IdAntiFreeze,
IdThreadComponent, IdFTP;

type
TThread1 = class(TThread)

private
fCount, tstart, tlast: integer;
tURL, tFile, temFileName: string;
tResume: Boolean;
tStream: TFileStream;
protected
procedure Execute; override;
public
constructor create1(aURL, aFile, fileName: string; bResume: Boolean; Count,
start, last: integer);
procedure DownLodeFile(); //下载文件
end;

type
TForm1 = class(TForm)
IdAntiFreeze1: TIdAntiFreeze;
IdHTTP1: TIdHTTP;
Button1: TButton;
ProgressBar1: TProgressBar;
IdThreadComponent1: TIdThreadComponent;
Label1: TLabel;
Label2: TLabel;
Button2: TButton;
Button3: TButton;
ListBox1: TListBox;
Edit1: TEdit;
Edit2: TEdit;
Label3: TLabel;
Label4: TLabel;

procedure Button1Click(Sender: TObject);
procedure IdHTTP1WorkBegin(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCountMax: Integer);
procedure IdHTTP1Work(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCount: Integer);
procedure Button2Click(Sender: TObject);
procedure IdHTTP1Status(ASender: TObject; const AStatus: TIdStatus;
const AStatusText: string);
procedure Button3Click(Sender: TObject);
private
public
nn, aFileSize, avg: integer;
MyThread: array[1..10] of TThread;
procedure GetThread();
procedure AddFile();
function GetURLFileName(aURL: string): string;
function GetFileSize(aURL: string): integer;
end;

var
Form1: TForm1;

implementation
var
AbortTransfer: Boolean;
aURL, aFile: string;

tcount: integer; //检查文件是否全部下载完毕
{$R *.dfm}

//get FileName

function TForm1.GetURLFileName(aURL: string): string;
var
i: integer;
s: string;
begin //返回下载地址的文件名

s := aURL;
i := Pos('/', s);
while i <> 0 do //去掉"/"前面的内容剩下的就是文件名了
begin
Delete(s, 1, i);
i := Pos('/', s);
end;
Result := s;
end;

//get FileSize

function TForm1.GetFileSize(aURL: string): integer;
var
FileSize: integer;
begin
IdHTTP1.Head(aURL);
FileSize := IdHTTP1.Response.ContentLength;
IdHTTP1.Disconnect;
Result := FileSize;
end;

//执行下载

procedure TForm1.Button1Click(Sender: TObject);
var
j: integer;
begin
tcount := 0;
Showmessage('OK!主线程在执行,获得文件名并显示在Edit2中');
aURL := Edit1.Text; //下载地址
aFile := GetURLFileName(Edit1.Text); //得到文件名
nn := StrToInt(Edit2.Text); //线程数
j := 1;
aFileSize := GetFileSize(aURL);
avg := trunc(aFileSize / nn);
begin
try
GetThread();
while j <= nn do
b

egin
MyThread[j].Resume; //唤醒线程
j := j + 1;
end;
except
Showmessage('创建线程失败!');
Exit;
end;
end;
end;

//开始下载前,将ProgressBar1的最大值设置为需要接收的数据大小.

procedure TForm1.IdHTTP1WorkBegin(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCountMax: Integer);
begin
AbortTransfer := False;
ProgressBar1.Max := AWorkCountMax;
ProgressBar1.Min := 0;
ProgressBar1.Position := 0;
end;

//接收数据的时候,进度将在ProgressBar1显示出来.

procedure TForm1.IdHTTP1Work(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCount: Integer);
begin
if AbortTransfer then
begin
IdHTTP1.Disconnect; //中断下载
end;
ProgressBar1.Position := AWorkCount;
//ProgressBar1.Position:=ProgressBar1.Position+AWorkCount; //*******显示速度极快
Application.ProcessMessages;
//***********************************这样使用不知道对不对

end;

//中断下载

procedure TForm1.Button2Click(Sender: TObject);
begin
AbortTransfer := True;
IdHTTP1.Disconnect;
end;

//状态显示

procedure TForm1.IdHTTP1Status(ASender: TObject; const AStatus: TIdStatus;
const AStatusText: string);
begin
ListBox1.ItemIndex := ListBox1.Items.Add(AStatusText);
end;

//退出程序

procedure TForm1.Button3Click(Sender: TObject);
begin
application.Terminate;

end;

//循环产生线程

procedure TForm1.GetThread();
var
i: integer;
start: array[1..100] of integer;
last: array[1..100] of integer; //改用了数组,也可不用
fileName: string;
begin
i := 1;
while i <= nn do
begin
start[i] := avg * (i - 1);
last[i] := avg * i -1; //这里原先是last:=avg*i;
if i = nn then
begin
last[i] := avg*i + aFileSize-avg*nn; //这里原先是aFileSize
end;
fileName := aFile + IntToStr(i);
MyThread[i] := TThread1.create1(aURL, aFile, fileName, false, i, start[i],
last[i]);
i := i + 1;
end;
end;

procedure TForm1.AddFile(); //合并文件
var
mStream1, mStream2: TMemoryStream;
i: integer;
begin
i := 1;
mStream1 := TMemoryStream.Create;
mStream2 := TMemoryStream.Create;

mStream1.loadfromfile('设备工程进度管理前期规划.doc' + '1');
while i < nn do
begin
mStream2.loadfromfile('设备工程进度管理前期规划.doc' + IntToStr(i + 1));
mStream1.seek(mStream1.size, soFromBeginning);
mStream1.copyfrom(mStream2, mStream2.size);
mStream2.clear;
i := i + 1;
end;
mStream2.free;
mStream1.SaveToFile('设备工程进度管理前期规划.doc');
mStream1.free;
//删除临时文件
i:=1;
while i <= nn do
begin
deletefile('设备工程进度管理前期规划.doc' + IntToStr(i));
i := i + 1;
end;
Form1.ListBox1.ItemIndex := Form1.ListBox1.Items.Add('下在成功');

end;

//构造函数

constructor TThread1.create1(aUR

L, aFile, fileName: string; bResume: Boolean;
Count, start, last: integer);
begin
inherited create(true);
FreeOnTerminate := true;
tURL := aURL;
tFile := aFile;
fCount := Count;
tResume := bResume;
tstart := start;
tlast := last;
temFileName := fileName;
end;
//下载文件函数

procedure TThread1.DownLodeFile();
var
temhttp: TIdHTTP;
begin

temhttp := TIdHTTP.Create(nil);
temhttp.onWorkBegin := Form1.IdHTTP1WorkBegin;
temhttp.onwork := Form1.IdHTTP1work;
temhttp.onStatus := Form1.IdHTTP1Status;
Form1.IdAntiFreeze1.OnlyWhenIdle := False; //设置使程序有反应.
if FileExists(temFileName) then //如果文件已经存在
tStream := TFileStream.Create(temFileName, fmOpenWrite)
else
tStream := TFileStream.Create(temFileName, fmCreate);

if tResume then //续传方式
begin
exit;
end
else //覆盖或新建方式
begin
temhttp.Request.ContentRangeStart := tstart;
temhttp.Request.ContentRangeEnd := tlast;
end;

try
temhttp.Get(tURL, tStream); //开始下载
Form1.ListBox1.ItemIndex := Form1.ListBox1.Items.Add(temFileName +
'download');

finally
//tStream.Free;
freeandnil(tstream);
temhttp.Disconnect;
end;

end;

procedure TThread1.Execute;
begin
if Form1.Edit1.Text <> '' then
//synchronize(DownLodeFile)
DownLodeFile
else
exit;
inc(tcount);
if tcount = Form1.nn then //当tcount=nn时代表全部下载成功
begin
//Showmessage('全部下载成功!');
Form1.ListBox1.ItemIndex := Form1.ListBox1.Items.Add('正在合并删除临时文件');
Form1.AddFile;
end;
end;

end.



2006-1-16 13:02:30
查看评语???

2006-1-16 13:06:14 才学习delphi两周,太多的东西都不懂。现在这个只实现了没个线程下载一部分文件,关于文件的合并还没考虑好。以后继续。


2006-1-16 14:44:03 记录一:开始在TThread1.DownLodeFile过程里面我用的是temIdHTTP.Request.ContentRangeStart := tstart;
temIdHTTP.Request.ContentRangeEnd := tlast;
temIdHTTP.Get(tURL, tStream); //开始下载
结果非常令人气愤的是多个线程中,第一个线程始终是只创建文件,而不下载它应该下载的那一部分。后面的那些线程都把自己应该下载的部分下载了,我是初学者跟本找不到原因,只知道thread1先运行了,不过似乎没什么效果,然后程序继续运行thread2,3,4,5。。。最后一个运行结束,又返回运行thread1,结果里面的变量值全不消失了,然后报错,又是什么socket,又是access的,头都大了。把FreeOnTerminate := false;也不行,后来偶然试了一下动态创建temIdHTTP结果就好了。哈哈,,,,,,


2006-1-16 15:00:56 问题一:现在越来越觉得自己做的这个不是多线程,因为那个execute要调用TThread1.DownLodeFile方法的,而该方法中执

行temIdHTTP.Get(tURL, tStream)它,当下载一个几十,几百兆,甚至更大的文件时,如果你用10个线程,平均分配每个线程就是几十兆。可怕的是synchronize(DownLodeFile);
我是个新手,学了不到两周,以我以前学过的一点JAVA知识(因为我对delphi真的毛都不懂呢)
synchronize里面的代码块或函数是同步的,也就是说当某一线程进入该代码快后,期于线程只能等那个幸运的线程执行完那段代码后才可以进入该代码段。我前面已经说了,就算是10个线程,遇到大文件时平均每块也要几十兆,这样的话那个线程要用很久很久才能执行完。也就是说在执行synchronize(DownLodeFile)时就算了CPU大大把运行的权利给别的线程,但是,因为这个该死的同步捣鬼,他们只无奈的等待,等待,直到thread1下载完属于它的那一块,然后第二个线程才可以迫不及待的冲进synchronize(DownLodeFile)区域。。。。这样岂不是多个线程顺序下载同一个文件吗,还不如我用一个线程来的实在。
其实这样还不是最可怕的,我最怕的是万一第一个线程身单力薄的,干半道活累死了,那其他线程岂非永远也不能下载他们的东东拉。。。。唉,其实我只会这样一种同步的方法,还是照例JAVA,我知道我做的肯定跟那些多线程下载软件做的不一样,但是我真的想不出什么好方法。
如果有多线程HTTP下载经验的高手看到希望能指点一二。。


2006-1-16 15:11:57 问题二:唉,,我是傻傻的把那几部分文件下载到本地电脑上,然后再合并,然后在删除临时文件。我确实是这样想的,你们别笑话我。我最讨厌的就是文件处理,所以根本对这些一点都不懂。不过我想那些成名软件肯定不是这样做的,因为我那他们的临时文件只有一个。他们怎么做的。我郁闷。去了几个论坛,搜了多个帖子,提了几次问题,多线程HTTP下载没人理。。唉,,看看盒子吧,,看看CSDN吧,妈的长点脑子也能看到2003年以前是最火的时候,那个时候恢复多多,每个人甚至都自己做一遍把源码发上来,现在可好,我还看个2004年的问题没人回复呢,一瞧TMD原来是HTTP多线程下载,得,,我也心凉了,命运不忌,晚生几年。来大富翁一看,还好,相比之下还是比较好滴。。。。。。。赞同的举下手。。。。。
高高兴兴的注册,准备发问。。。。。。。
我靠!!!网站热情提示我:如果你是新注册的用户两天后再来提问。。。
当时我那心啊,是拔凉拔凉的。。
郁闷不说了,谁让咱生的晚,错了改革开放,共同进步的好时候了呢,现在学的人太多,问的问题也水准差点,好象高手都不喜欢回答了,,唉,,,,也对,可能都去陪老婆孩子去了。


唉,想当初,,我可是热血青年,玩网游还义务帮GM回答玩家问题呢。。。算了,谁让咱突然热血沸腾想要重新做人呢。。努力吧。。。


2006-1-16 18:26:23 记录2:说实话,我对文件操作一点不懂,不过在网上找到一个关于文件合并的例子,里面有这么几句,被我偷学来
i:=1;
mStream1 := TMemoryStream.Create;
mStream2 := TMemoryStream.Create;

mStream1.loadfromfile('D:\Program Files\Borland\Delphi7\rui\http2oneUnit\readme.rtf'+'1');
while ibegin
mStream2.loadfromfile('D:\Program Files\Borland\Delphi7\rui\http2oneUnit\readme.rtf'+IntToStr(i+1));
mStream1.seek(mStream1.size,soFromBeginning);
mStream1.copyfrom(mStream2,mStream2.size);
mStream2.clear;
i:= i+1;
end;
mStream2.free;
mStream1.SaveToFile('d:readme.rtf');
mStream1.free;
唉,,感谢前人的无私奉献。


2006-1-16 18:29:31 问题三:因为我对文件操作一点不懂,所以分割方法一定是错了,因为我把文件合并后多了几个字节。不能使用了郁闷。它咋就错了呢,我真是不明白。
while i<=nn do
begin
if i=nn then
begin
start := avg*(i-1);
last := aFileSize; //有这部分,下载为1,2,3,4。。不用的话是2,3,4,,,1
end
else
start := avg*(i-1);
last := avg*i;
fileName:=aFile+IntToStr(i);
MyThread[i]:=TThread1.create1(aURL, aFile,fileName, false , i,start,last);
i :=i+1;
end;


2006-1-16 18:34:23 问题四:想通过每完成一个线程就把全局变量tcount加一,当tcount=nn线程数时,通知系统全部下载成功,可惜对消息什么的一点不懂。无论是在idhttp1的onwork还是在onworkend里面随时检查tcount,但是始终没成功。郁闷。。。


2006-1-18 15:04:09 解决问题三:有位哥哥告诉我当i自加到等于线程数nn的时候,让
last:=avg*i+aFileSize-avg*nn;
当i<>nn时last:=avg*i-1;
唉,我的系统ghost了一下,可惜安装盘没有带来,米了iis也不能试一下,明天试试看。
真是搞不清楚last:=aFileSize跟上面有什么区别吗??


2006-1-19 16:07:03 解决问题四:
1。因为不知道delphi是大小写不敏感的,以至于tcount跟tCount的作用没起到。
现在可以通过判断tcount来确定个部分下载结束,并进行合并,删除临时文件。
2。temhttp.Disconnect;添加了此句。因为以前没有释放连接,导致当重复点击下载时,会发生找不到服务器文件,原因是服务器连接数量超过。
3在线程中使用showmessage()经常出现Canvas does not allow drawing错误。只好把把提示写在了listbox里面了。
3。下在多个临时文件,合并再删除,这是最笨的方法。一直想找个好的方法,不过对文件操作真的是一点不懂,所以还没头绪。听人说过用一个临时文件

就行,但不知道怎么弄,。


2006-1-20 10:06:51 备注:编程序时很多问题都是出在基础知识不了解那里,想最初不知道怎么把两个类下在同一个Unit里,看过一段网上代码之后才试在写在一里面的,省去很多麻烦,初学delphi看那个类(姑且先这样叫吧)的定义很不习惯。现在才知道implementation 跟public下的变量的区别,implementation下的变量叫全局的,在别的单元里去访问不到,感觉这名字会误导新学的人。看那个文档很不习惯,重写个函数还总是出什么overload错误。Type定义个新的数组用的时候老是说没定义过,也许是看的资料年头太久远了,不过还好,自己可以写方法跟过程了,就是感觉很别扭。可能是以前没用过的原因。这些天,每天都在找IDHTTP,多线程。却发现应该仔细看看基础知识,pascal,等。程序功能基本实现了。想做的更改确实现在的自己不能做到的。所以想看看基础的知识,现在上网问个问题也费劲。各个技术论坛最火的时候好象是2003年以前,上网搜的资料80%标着2003年以前写的。真是郁闷,未啥晚生两年,错过了这场学习技术的热潮。论坛上新手太多,问的问题,连我都脸红,难怪高手都不爱去了,唉,也许是都回家陪老婆孩子去了。
还好,上帝还没抛弃我。认识未大哥,很是照顾,分割文件那个错误就是他发现的。把自己的程序发到大富翁论坛上,做了笔记。把自己心中的疑问也都提了出来,结果让人好顿鄙视。晕了,哥哥我要不是才学两天的菜鸟,哪会有那么多疑问。
自己的事自己做。唉,晚生两年。


相关主题
相关文档
最新文档