最近写一个小东西,要求有在线更新功能,碰到这样的问题首先想到了Msxml2.XMLHTTP和ADODB.Stream,最后同步下载到是实现了,但却被异步下载难倒了,后来找了大量的VC、VB、ASP、.NET等代码来参考,但始终很难在VF上直接实现,后来又转想到了WININET(Microsoft Internet Transfer Control )控件,大家知道inet也有同步和异步两种方式,我们常用的获得网页源码的方法如:
thisform.inet.openurl(www.163.com')这样我们就得到了163主页的源代码,同样thisform.inet.openurl('http://www.vfptop.com/download/ydgl.rar',1)
这样的话我们就获得了ydgl.rar文件的全部内容,再新建立一个文件把得到的内容写进去就算是把ydgl.rar这个文件下载下来了,但是这样做是一次性把整个文件的内容全部下载,没办法实现进度表达,所以我们要采用异步方式来获取,也就是inet.execute(url),知道了这些就可以写代码了:

实现进度显示的步骤:
1、首先获取被下载文件的大小
我们知道在访问一个URL的时候会首先返回一个HTTP头文件,这文件头里包含有被下载文件相关重要信息,如这样一个THHP头:
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Wed, 16 Aug 2006 09:20:34 GMT
Content-Type: application/octet-stream
Accept-Ranges: bytes
Last-Modified: Mon, 14 Aug 2006 01:50:10 GMT
ETag: "3ac82dfa43bfc61:dae"
Content-Length: 10248
这里的Content-Length头部分显示的就是我们要下载文件的大小,这个数值我们可以用
inet.getheader(‘Content-Length’)来获得。
获得被下载文件大小的代码:
inet. execute(url)
inet.getheader(‘Content-Length’)&&返回文件大小,字符型。

2、按段下载数据
知道了文件的大小,接下来就是要一段一段的来获取文件的数据,比如已知一个文件的大小是10M(10*1024*1024bety),我们下载的时候每次按1024KB来下载,那么一共要下载10次才能下载完,如果我们把这10次下载的任务交给10个inet去完成,那就变成了‘多线程’下载了,当然这里的‘多线程’实际上是多任务下载不是真正的多线程。
inet.execute(url,'get',,’ Range: bytes=0-’)这句的意思是获取被下载文件的头部一直尾部的全部数据,也就是整个文件。
inet.execute(url,'get',,’ Range: bytes=1024-2101’)这句的意思是获取被下载文件的1024字节到2101字节的部数据。
这样我们就可以很好的给inet分配下载段了。
inet.execute()获取了数据后,我们再用inet.GetChunk(size,1)来从缓冲区获取数据,这里的size是指你要获得的字节数。
这样我们就可以一段一段的下载文件了,实现进度条显示就解决了
3、显示进度信息
进度信息基本上靠一个timer就能刷新下载剩余量、下载百分比、下载剩余时间等进度信息了,这里就不多说了。
4、一些需要注意的地方
a. 分段下载的过程是个循环下载的过程,但我们却不能用for或者do whil这样的循环去操作,因为下载每一断的时候都是有延时的,由于VF是单线程顺序执行代码,所以这样操作一般都会失败,那么我们就采用计数变量来不逐一循环激发 inet.execute ()和inet.GetChunk(),比如我们是分100段来下载文件,当计数变量<=100的情况下就循环激发,也就是 inet.GetChunk()后再去激发inet.execute()然后inet.execute()又去激发inet.GetChunk(),达到 100的时候我们再看下这时候文件是否全部下载完毕,如果没有那么再把剩下的数据补进去就行了。
b.inet.GetChunk()获取的数据类型是和文件数据类型一致的,所以我们直接fcraete()建立一个文件,然后把获得数据直接fwrite() 就可以了。
c.一般最少用两个inet来操作会更方便点,一个是获取文件信息,一个是专门下载数据。
d. 每个inet执行结束后最好inet.objiect.cancel,因为有些被下载的文件不支持多线程下载。
e.段点续载你可以象flashget等软件先建立一个自己的文件结构,等下载完毕后再还原文件,如果补这样的话,比如有和你上次没下载完的文件同名的文件就补会看作是自己没下载完的文件了,续载的时候先检查下已存在的同名文件是否是自己以前下载过的文件这种情况就可以避免了。

想到那里说到那里,难免有错误的地方!
如果自动下载,不需要显示的话,最简单的方法就是用API函数:

DECLARE INTEGER URLDownloadToFile IN urlmon.dll;
    INTEGER pCaller, STRING szURL,;
    STRING szFileName, INTEGER dwReserved,;
    INTEGER lpfnCB
LOCAL cRemoteFile, cLocalFile, nResult
cRemoteFile = "http://meizibbs.3322.org/bbs/images/logo.GIF" &&文件静态地址
cLocalFile = "123.gif" &&本地文件名
WAIT WINDOW NOWAIT "Downloading file..."
nResult = URLDownloadToFile(0, cRemoteFile, cLocalFile, 0,0)
WAIT CLEAR
         IF nResult = 0
    ? "下载成功!"
ELSE
    ? "下载出错,出错代码:", nResult
ENDIFVFP Code: DOWNLAOD.PRG
**代码由行者SCX2PRG.PRG自动生成**
Public _form1
_form1=CreateObject("form1")
_form1.show
RETURN
DEFINE CLASS form1 AS form
Top = 90
Left = 294
Height = 183
Width = 343
DoCreate = .T.
Caption = "行者示例-文件下载"
BackColor =RGB( 153,153,153)
Name = "form1"
PROCEDURE down
X=ALLTRIM(STR(PD*102400))
IF X==''
X='0'
endi
Y=ALLTRIM(STR((PD+1)*102400-1))
f_Header='Range: bytes='+X+'-'+Y
IF PD>f_cs
f_header='Range: bytes='+X+'-'
thisform.inet2.execute(f_url,'get',,f_Header)
ELSE
thisform.inet2.execute(f_url,'get',,f_header)
ENDIF
ENDPROC
PROCEDURE Init
this.AddObject("Dataenvironment","_classname1")
this.AddObject("Inet1","_classname2")
this.AddObject("Command3","_classname3")
this.AddObject("inet2","_classname4")
this.AddObject("Label2","_classname5")
this.AddObject("Shape1","_classname6")
this.AddObject("Shape2","_classname7")
this.AddObject("Shape3","_classname8")
this.AddObject("Label5","_classname9")
this.AddObject("Label6","_classname10")
this.AddObject("Label1","_classname11")
this.AddObject("Label3","_classname12")
this.AddObject("Label4","_classname13")
this.AddObject("Timer1","_classname14")
PUBLIC f_url,f_size,f_cs,f_sy,f_name,PD,h,f_header,X,Y,JS
thisform.shape3.Width=0
ENDPROC
ENDDEFINE
DEFINE CLASS _classname1 AS dataenvironment
Visible=.T.
Top = 0
Left = 0
Width = 0
Height = 0
DataSource = .NULL.
Name = "Dataenvironment"
ENDDEFINE
DEFINE CLASS _classname2 AS olecontrol
Visible=.T.
Comment = ""
Top = 15
Left = 127
Height = 24
Width = 38
Name = "Inet1"
oleclass="InetCtls.Inet.1"
PROCEDURE StateChanged
*** ActiveX 控件事件 ***
LPARAMETERS state
DO case
CASE state=1
thisform.label1.caption='下载状态:正在连接服务器下载地址.....'
*!*    CASE state=2
*!*    thisform.label1.caption='连接服务器成功'
*!*    CASE state=3
*!*    thisform.label1.caption='该控件正在与主机连接。 '
*!*    CASE state=4
*!*    thisform.label1.caption='该控件已与主机连接成功。'
*!*    CASE state=5
*!*    thisform.label1.caption='该控件正在向主机发送请求。'
*!*    CASE state=6
*!*    thisform.label1.caption='该控件发送请求已成功。'
*!*    CASE state=7
*!*    thisform.label1.caption='该控件正在接收主机的响应。 '
*!*    CASE state=8
*!*       thisform.label1.caption='该控件已成功地接收到主机的响应。 '
*!*    CASE state=9
*!*    thisform.label1.caption='该控件正在解除与主机的连接。'
*!*    CASE state=10
*!*    thisform.label1.caption='该控件已成功地与主机解除了连接。 '
CASE state=11
thisform.label1.caption='下载状态:与主机通讯时出现了错误!'
CASE state=12
thisform.label1.caption='下载状态:正在下载该文件.....'
f_size=VAL(thisform.inet1.getheader('Content-Length'))
thisform.label2.Caption='文件大小:'+ALLTRIM(STR(f_size))+' byte'
f_cs=INT(f_size/102400)
f_sy=MOD(f_size,102400)
h=FCREATE(f_name)thisform.timer1.Interval=1000
PD=0
thisform.down()
thisform.inet1.object.Cancel
endc
ENDPROC
ENDDEFINE
DEFINE CLASS _classname3 AS commandbutton
Visible=.T.
Top = 140
Left = 150
Height = 25
Width = 60
Caption = "开始下载"
Name = "Command3"
PROCEDURE Click
f_url='http://www.quanfree.com/adfile/setupol_3247_0172.exe'&;&被下载文件地址
f_name='ad.zip'&&被存放到本地的文件名
thisform.shape3.Width=0
JS=0&&下载秒数计数
thisform.inet1.execute(f_url)
ENDPROC
ENDDEFINE
DEFINE CLASS _classname4 AS olecontrol
Visible=.T.
Comment = ""
Top = 15
Left = 178
Height = 24
Width = 38
Name = "inet2"
oleclass="InetCtls.Inet.1"
PROCEDURE StateChanged
*** ActiveX 控件事件 ***
LPARAMETERS state
IF state=12
IF PD>=f_cs
st=thisform.inet2.GetChunk(f_sy,1)
=FWRITE(h,st)
=FCLOSE(h)
thisform.timer1.Interval=0
thisform.label6.Caption='100%'
thisform.Caption='文件下载完毕'
thisform.label1.caption='下载状态:'
thisform.label2.caption='下载状态:'
=MESSAGEBOX('下载完毕')
*!*    thisform.inet1.object.Cancel
else
st=thisform.inet2.GetChunk(102400,1)
PD=PD+1
=FWRITE(h,st)
thisform.shape3.Width=(PD/f_cs)*246
thisform.down()
endi
endi
ENDPROC
ENDDEFINE
DEFINE CLASS _classname5 AS label
Visible=.T.
AutoSize = .T.
FontSize = 11
BackStyle = 0
Caption = "文件大小:"
Height = 19
HelpContextID = 1
Left = 7
Top = 31
Width = 77
Name = "Label2"
ENDDEFINE
DEFINE CLASS _classname6 AS shape
Visible=.T.
Top = 105
Left = 76
Height = 17
Width = 248
BackStyle = 0
Curvature = 10
Name = "Shape1"
ENDDEFINE
DEFINE CLASS _classname7 AS shape
Visible=.T.
Top = 104
Left = 75
Height = 17
Width = 248
BackStyle = 0
Curvature = 10
BorderColor =RGB( 255,255,255)
Name = "Shape2"
ENDDEFINE
DEFINE CLASS _classname8 AS shape
Visible=.T.
Top = 106
Left = 77
Height = 15
Width = 246
BackStyle = 1
BorderStyle = 0
BorderWidth = 0
Curvature = 10
BackColor =RGB( 128,128,128)
Name = "Shape3"
ENDDEFINE
DEFINE CLASS _classname9 AS label
Visible=.T.
AutoSize = .T.
FontSize = 11
BackStyle = 0
Caption = "下载进度:"
Height = 19
Left = 7
Top = 105
Width = 77
Name = "Label5"
ENDDEFINE
DEFINE CLASS _classname10 AS label
Visible=.T.
AutoSize = .T.
BackStyle = 0
Caption = "%"
Height = 16
Left = 204
Top = 106
Width = 8
Name = "Label6"
ENDDEFINE
DEFINE CLASS _classname11 AS label
Visible=.T.
AutoSize = .T.
FontSize = 11
BackStyle = 0
Caption = "下载状态:"
Height = 19
Left = 7
Top = 7
Width = 77
Name = "Label1"
ENDDEFINE
DEFINE CLASS _classname12 AS label
Visible=.T.
AutoSize = .T.
FontSize = 11
BackStyle = 0
Caption = "下载速率:"
Height = 19
Left = 7
Top = 56
Width = 77
Name = "Label3"
ENDDEFINE
DEFINE CLASS _classname13 AS label
Visible=.T.
AutoSize = .T.
FontSize = 11
BackStyle = 0
Caption = "剩余时间:"
Height = 19
Left = 7
Top = 80
Width = 77
Name = "Label4"
ENDDEFINE
DEFINE CLASS _classname14 AS timer
Visible=.T.
Top = 27
Left = 231
Height = 23
Width = 23
Interval = 0
Name = "Timer1"
PROCEDURE Timer
JS=JS+1
sl=ALLTRIM(STR(PD*102400/1024/JS,6,2))
thisform.label3.caption='下载速率:'+sl+' KB/S'
thisform.label4.Caption='剩余时间:'+ALLTRIM(STR((f_size-PD*102400)/1024/VAL(sl)))+' 秒'
thisform.label6.Caption=ALLTRIM(STR((PD*102400/f_size)*100,5,2))+'%'
thisform.Caption='行者示例-文件下载     '+ALLTRIM(STR((PD*102400/f_size)*100,5,2))+'%'
ENDPROC
ENDDEFINE