pbai 发表于 2026-8-27 07:24:38

PBIDEA:用 uo_httpclient 做大文件下载并显示实时进度

PBIDEA:用 uo_httpclient 做大文件下载并显示实时进度


阅读说明
1. 适用版本:PBIDEA 1.x(基于 PB 12.5 运行库;uo_httpclient 由 PbIdea.dll 提供,需安装 PBIDEA 运行库)。本文示例依赖 PBIDEA 运行库,经典 PB(未装 PBIDEA)无此对象。
2. 支持数据库:不涉及数据库(本文为 HTTP 文件下载,与 DBMS 无关,故不罗列)。
3. 操作系统与环境要求:Windows 7+,已安装 PBIDEA 运行库(PbIdea.dll 等);工程中需导入含 uo_httpclient 的 pbl(websuite.pbl)。
4. 难度系数:★★★☆☆
5. 其它阅读说明:建议先了解 Day4《uo_httpclient 发请求》的 GET/POST 基础;进度示例需在窗口上放置进度条控件 p_1 与多行编辑框 mle_1(仅进度示例需要,基础下载示例不需要任何控件)。


一、核心概念与原理

在 PBIDEA 里做 HTTP 下载,主力对象是 uo_httpclient(位于 websuite.pbl,底层基于 Windows 的 WinHTTP)。它把"下载到磁盘"和"下载到内存"两套能力都封装好了,而且内置了进度回调事件,让我们在下载大文件时能把实时进度显示给用户,而不是卡在一个没有反馈的界面上。

下载这件事有两个最关键的落点:


[*]落盘还是落内存:DownLoadFile(...) 直接把远程文件写到本机磁盘;DownLoad(...) 把内容读进 blob,由你自己决定怎么处理。大文件一般直接落盘,体积小或需要 Immediate 加工的内容才用 blob。
[*]同步 + 事件回调:DownLoadFile / DownLoad 本身是同步调用——函数不返回就代表还没下完。但在下载过程中,它会周期性触发 ondownload 事件,把"总字节数 / 已下字节数 / 百分比 / 当前文件名"四个参数传进来;下载结束时再触发 onfinish 事件。也就是说,界面反馈靠事件,控制流靠同步返回。


百分比参数 ai_percent 是 integer(0~100),两个字节数参数是 double(因为大文件字节数会超过 long 上限,用 double 兜底)。ondownload 返回 TRUE 表示继续下载,FALSE 表示中止下载——这正是"取消按钮"的实现入口。

二、uo_httpclient 下载相关 API 全貌

下载主题相关的接口、事件、常量清单如下(均来自 websuite.pbl/uo_httpclient.sru 真实导出):

下载函数(PbIdea.dll 原生)

[*]boolean DownLoadFile(readonly string strUrl) —— 下载到当前目录下的 .\download\ 子目录(目录需存在)。
[*]boolean DownLoadFile(readonly string strUrl, readonly string strSavePath) —— 下载并保存到指定完整路径(含文件名)。
[*]boolean DownLoadFile(readonly string strUrl, readonly string strSavePath, boolean bTriggerEvent) —— 同上,第三个参数 TRUE 时才触发 ondownload / onfinish 事件。
[*]blob DownLoad(readonly string strUrl) —— 下载到内存 blob(不落盘)。
[*]blob DownLoad(readonly string strUrl, boolean bTriggerEvent) —— 同上,第二个参数控制是否触发进度事件。


下载相关事件

[*]event type boolean ondownload(double ad_totalsize, double ad_downsize, integer ai_percent, string as_file) —— 进度回调,返回 TRUE 继续、FALSE 中止。
[*]event onfinish(boolean success, string info) —— 下载结束回调,success 表示成败。


常用配套设置(下载前调用)

[*]SetProxy(readonly string ip, int port) —— 设置代理。
[*]SetTimeout(ulong nTimeout) 或 SetTimeout(ulong nConn, ulong nSend, ulong nRecv) —— 超时(单位毫秒)。
[*]SetBasicAuth(username, password) / SetJWTAuth(token) —— 鉴权(私有仓库 / 内网服务常用)。
[*]SetHeader(key, value) / SetHeader(uo_map) —— 自定义请求头(例如带 token、设 Range)。


动作常量(Request 用,下载一般用不到但列出来备查)

[*]HttpGet=1 HttpPost=2 HttpPut=3 HttpPatch=4 HttpDelete=5 ……HttpHead=7 等。



注意:uo_httpclient 是 global uo_httpclient uo_httpclient(自动实例化全局变量),所以你也可以直接写 uo_httpclient.DownLoadFile(...)。但如果要写 ondownload 事件代码,必须新建一个继承自 uo_httpclient 的子类 NVO,把代码写在子类的事件里——全局自动实例无法挂自定义事件体。


三、典型用法示例

示例 1:最基础——直接下载落盘


前置:工程中已导入 websuite.pbl(含 uo_httpclient),运行环境已装 PBIDEA 运行库。无界面控件要求,可在按钮 clicked 或任意脚本处运行。
步骤:把下方代码贴入按钮 clicked 事件,运行并点击按钮,即可在 MessageBox 看到下载成败。



// 示例输入:远程文件地址
string ls_url
ls_url = 'https://example.com/release/bigfile.zip'
// 示例输入:本机保存的完整路径(目录必须已存在)
string ls_save
ls_save = 'C:\temp\bigfile.zip'
// HTTP 客户端对象
uo_httpclient hc
hc = create uo_httpclient
// 下载结果
boolean lb_ok
lb_ok = false

// 两参重载:不触发进度事件,直接落盘
lb_ok = hc.DownLoadFile(ls_url, ls_save)

if lb_ok then
    MessageBox('下载完成', '文件已保存到:' + ls_save)
else
    MessageBox('下载失败', '请检查 URL 是否可达、保存目录是否存在')
end if

destroy hc


示例 2:带实时进度——用子类挂 ondownload 事件


前置:新建一个 NVO(如 uo_httpclient_prog)继承自 uo_httpclient,在其 ondownload 事件写进度刷新代码(见下方"事件体");窗口 w_demo 上放进度条控件 p_1 与多行编辑框 mle_1,并把 p_1 的 minposition=0、maxposition=100。
步骤:1) 建子类并写事件体;2) 在窗口按钮 clicked 贴入"调用代码";3) 运行窗口点击按钮,即可看到进度条与文字实时刷新。


事件体(写在 uo_httpclient_prog 的 ondownload 事件中):事件四个入参为 ad_totalsize(总字节数,double)、ad_downsize(已下字节数,double)、ai_percent(百分比 0~100,integer)、as_file(当前文件名);返回 TRUE 继续下载、FALSE 中止。下面把进度刷新到窗口控件 p_1(进度条)与 mle_1(多行编辑框):


p_1.position = ai_percent
mle_1.text = '已下载 ' + String(ad_downsize) + ' / ' + String(ad_totalsize) + ' 字节(' + String(ai_percent) + '%)'
return TRUE


调用代码(窗口按钮 clicked 事件):


// 示例输入:远程文件地址
string ls_url
ls_url = 'https://example.com/release/bigfile.zip'
// 示例输入:本机保存的完整路径
string ls_save
ls_save = 'C:\temp\bigfile.zip'
// 继承了 ondownload 事件的子类实例
uo_httpclient_prog hc
hc = create uo_httpclient_prog

// 三参重载:第三个参数 TRUE 才会触发 ondownload / onfinish 事件
hc.DownLoadFile(ls_url, ls_save, TRUE)

// 走到这里下载已经结束(期间进度条已实时刷新)
MessageBox('提示', '下载已结束,结束时会触发 onfinish 事件')
destroy hc


示例 3:下载到内存 blob 再落盘


前置:同示例 1,需 websuite.pbl 与 PBIDEA 运行库;本例额外用到 uo_file.BlobToFile(websuite.pbl,真实存在)把 blob 写盘。
步骤:把下方代码贴入按钮 clicked 事件,运行后可在 MessageBox 看到下载字节数。



// 示例输入:远程文件地址(这里以下载一个 PDF 为例)
string ls_url
ls_url = 'https://example.com/docs/report.pdf'
// 示例输入:本机保存路径
string ls_save
ls_save = 'C:\temp\report.pdf'
// 内存中的文件内容
blob lbl_data
// HTTP 客户端对象
uo_httpclient hc
hc = create uo_httpclient
// 文件工具对象(PBIDEA 自带,BlobToFile 真实存在)
uo_file uf
uf = create uo_file
// 写盘结果
boolean lb_ok
lb_ok = false

// 一参(URL)+ 二参(TRUE 触发进度事件):内容读进 blob,不落盘
lbl_data = hc.DownLoad(ls_url, TRUE)

if Len(lbl_data) > 0 then
    // 把 blob 写成文件
    lb_ok = uf.BlobToFile(lbl_data, ls_save)
    MessageBox('下载完成', '字节数:' + String(Len(lbl_data)))
    destroy uf
else
    MessageBox('下载失败', '返回内容为空,请检查 URL 与网络')
end if

destroy hc


四、边界情况与易错点


易错点现象 / 后果正确处理
bTriggerEvent 没传 TRUE下载能成功,但 ondownload / onfinish 永不触发,进度条不动用三参 DownLoadFile(url, path, TRUE) 或两参 DownLoad(url, TRUE)
保存路径目录不存在DownLoadFile 返回 FALSE,文件没生成先 uo_dir / DirectoryExists 建好目录再下载
单参 DownLoadFile(url)落到当前目录 .\download\,路径不在你预期的位置明确传第二参完整路径,别依赖默认目录
在 UI 线程直接下载大文件函数同步阻塞,下载期间窗口"假死"、进度条不重绘把下载丢到 uo_thread 后台线程(见第六节),或至少提示用户不要操作
ondownload 返回 FALSE 忘了处理下载被中止却没提示,用户以为卡住onfinish 的 success 参数能反映是否成功,用它统一收尾
忘了 destroy hc每次下载都留一个 WinHTTP 句柄,长期运行句柄泄漏下载结束后 destroy hc(示例均已包含)
代理 / 超时没设内网下载超时无反馈、或走不通代理下载前调用 SetProxy / SetTimeout(毫秒)


额外提醒:两个字节数参数是 double,显示时用 String(...) 即可;百分比 ai_percent 是 integer,直接赋给进度条 position 就行,无需换算。

五、与其它下载方案对比


方案对象特点何时选
HTTP 下载(本文)uo_httpclient原生 WinHTTP,支持进度事件、代理、鉴权,最通用绝大多数从 http/https 拉文件
带进度对话框的下载器uo_downloader(继承 uo_curl)自带 uo_progress 悬浮进度窗 + 异步消息(onfilestart/onprogressdown/onfileend),开箱即用想少写代码、要现成进度 UI
底层可定制下载uo_curluo_downloader 的父类,能力最全但需自己接事件需要 curl 级控制(断点、限速等)
FTP / SFTP 文件传输uo_ftp / uo_sftp走 FTP/SFTP 协议,不是 HTTP服务器只提供 FTP/SFTP 时


个人经验:如果只是"下一个文件并显示进度",uo_httpclient 三参 DownLoadFile + 子类事件已经足够;如果连进度对话框都不想自己画,uo_downloader.download(title, url, path) 一行搞定(它内部用 uo_progress 弹窗 + 异步消息,WaitAsyncMessageFinish 等下载完成)。但要记住 uo_downloader 底层是 uo_curl,与 uo_httpclient 不是同一个对象,不要混用 API。

六、进阶扩展

1. 后台下载,界面不卡(推荐做法)

DownLoadFile 是同步的,大文件会阻塞 UI 线程。把下载丢进 uo_thread 后台线程,主线程照常响应;进度通过线程安全的全局变量 / 消息回传主线程刷新进度条。要点:


[*]下载函数体放在一个 NVO 的方法里,用 uo_thread_factory.StartThread("uo_downloader_job", "of_download", ...) 启动;
[*]跨线程只传标量(url、路径、百分比),不要跨线程直接操作窗口控件;
[*]主线程用 Timer 或自定义消息定时读取"已下字节数"刷新进度条。


2. 取消下载

在 ondownload 事件里判断一个实例变量(如 ib_cancel),用户点"取消"按钮时把它置 TRUE,事件返回 FALSE 即可中止当前下载。

3. 带鉴权 / 自定义头

下载私有仓库或内网接口前,先 hc.SetBasicAuth('user','pass') 或 hc.SetHeader('Authorization', 'Bearer ' + ls_token),再调 DownLoadFile。头要在下载函数之前设置好。

4. 关于断点续传

uo_httpclient 的公开 API 里没有暴露"断点续传 / Range 续传"接口。真要做续传,只能自己 SetHeader('Range', 'bytes=' + 已下字节 + '-') 并用 DownLoad 取剩余片段再拼接——但服务器是否支持 Range、PbIdea 是否透传该头都需要实测,不保证可用,生产环境慎用。需要稳定的断点续传,建议改用 uo_curl 体系或自己用 uo_file 分块读写实现。

小结:uo_httpclient 做大文件下载的核心是"三参 DownLoadFile(url, path, TRUE) + 子类 ondownload 事件":事件给进度、同步给结果、返回 FALSE 给取消。记住设好保存目录、及时 destroy、大文件放后台线程,下载功能就稳了。
页: [1]
查看完整版本: PBIDEA:用 uo_httpclient 做大文件下载并显示实时进度

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com