马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?站点注册
×
PBIDEA:用 uo_zip 与 uo_7z 在 PowerBuilder 里做 ZIP 与 7z 压缩解压
阅读说明
1. 适用版本:PB 12.5 及以上(含 PBIDEA 1.x;uo_zip / uo_7z 依赖 PbIdea.dll 运行库)
2. 支持数据库:本文为纯文件操作,不涉及数据库
3. 操作系统与环境要求:Windows 7+,已安装 PBIDEA 运行库(PbIdea.dll 等)
4. 难度系数:★★★☆☆
5. 其它阅读说明:示例中的路径请替换为你本机真实路径;uo_zip 与 uo_7z 位于 websuite.pbl,需将该 pbl 加入工程,并随应用一起分发 PbIdea.dll
做桌面应用绕不开"打包":导出一批报表、把日志归档、把更新包发给客户、把多个文件合成一个附件上传……这些场景在 PB 里不需要调用外部 exe,PBIDEA 的 uo_zip 和 uo_7z 两个对象就能直接搞定。一个产出通用的 ZIP(Windows 资源管理器原生能打开),一个产出压缩率更高的 7z。本文把这两个对象的全部 API、典型用法、内存压缩、目录遍历配合、以及常见坑一次讲透。
一、两个对象各自的定位
- uo_zip:封装 ZIP 格式的压缩与解压。支持加密码、加注释、按条目读取、把 blob 直接打包(不落盘)。底层是 PbIdea.dll 的 zip* 系列函数,对象 constructor 自动 zipCreate()、destructor 自动 zipDestroy(),所以必须 create / destroy,不能当 autoinstantiate 用。
- uo_7z:封装 7z 格式的压缩与解压(基于 7-Zip 内核)。除了能压整个目录/文件成 .7z,还提供 blob 级别的内存 7z 压缩(自定义格式,注意只能用 Uncompress7Z 还原)。同样是 constructor 自动 SevenZipCreate()、destructor 自动 SevenZipDestroy(),也要 create / destroy。
一句话选型:要兼容性选 uo_zip(ZIP),要更高压缩率选 uo_7z(7z);如果只是想在网络传输前把一段二进制数据压小,两个对象都支持纯内存 blob 压缩。
二、uo_zip 完整 API 清单
下面是 uo_zip 对外暴露的全部方法(均来自 PbIdea.dll,已逐一核对源码原型):
| 方法 | 说明 | | boolean Open(string strZipFile) | 打开(不存在则创建)压缩包,无密码 | | boolean Open(string, boolean bReadOnly) | 指定是否只读 | | boolean Open(string, boolean bReadOnly, string strPassword) | 带密码打开(创建加密包或读加密包) | | boolean Open(blob zipBuffer, string strPassword) | 直接从内存 blob 只读打开 | | boolean IsOpen() | 是否已打开 | | Close() | 提交所有操作并真正写入磁盘(相当于 commit) | | Discard() | 放弃本次操作(相当于 rollback) | | boolean Add(string strZipFile) | 加入一个物理文件 | | long AddPath(string strPath) | 加入整个目录(自动保留子目录结构) | | boolean Add(string strName, blob data) | 把一段 blob 作为条目加入(不依赖物理文件) | | long GetEntryCount() | 条目总数 | | boolean EntryExist(string strName) | 某条目是否存在 | | boolean DeleteEntry(string strName) | 删除条目 | | boolean RenameEntry(string strName, string strNewName) | 重命名条目 | | long GetEntryList(ref ...7个数组) | 枚举全部条目元信息 | | blob ReadBlob(long index) / blob ReadBlob(string strName) | 按索引或名称读取条目内容为 blob | | long Extract(string strPath) | 解压全部到目录 | | long Extract(string strPath, long index) | 按索引解压单个条目 | | long Extract(string strPath, string strName) | 按名称解压单个条目 | | SetComment / GetComment | 压缩包注释 | | DeleteFile() | 删除整个 zip 文件 | | blob ReadFile(string file) | 工具函数:读文件为 blob | | boolean WriteFile(blob data, string file) | 工具函数:把 blob 写成文件 |
最关键的一点:Add* 之后必须调用 Close() 才会真正生成文件;中途想放弃调用 Discard()。这和数据库的事务提交/回滚是一个道理。
三、uo_zip 压缩:把目录和文件打进 ZIP
前置:工程已加入 websuite.pbl 并随应用分发 PbIdea.dll;下面代码放在某个按钮的 clicked 事件里,把路径换成你本机真实存在的目录/文件即可直接运行。 - // 示例输入:要生成的 zip 文件完整路径(不存在会被创建)
- string ls_zip
- ls_zip = 'D:/temp/demo.zip'
- // 示例输入:要加入压缩包的单文件完整路径
- string ls_file
- ls_file = 'D:/temp/report.txt'
- // 示例输入:要整目录打包的源目录(含子目录一并保留)
- string ls_srcdir
- ls_srcdir = 'D:/temp/sourcedir'
- uo_zip lz
- lz = create uo_zip
- // 打开压缩包(无密码);返回 true 表示打开成功
- boolean lb_open
- lb_open = lz.Open(ls_zip)
- // 加入单个文件
- lz.Add(ls_file)
- // 加入整个目录,目录结构会被保留进压缩包
- lz.AddPath(ls_srcdir)
- // 只有 Close 才真正把数据写入磁盘(相当于 commit)
- lz.Close()
- destroy lz
- MessageBox('压缩完成', '已生成:' + ls_zip)
复制代码
如果要做加密 zip,把 Open 换成带密码的重载即可:
- // 示例输入:zip 输出路径
- string ls_zip
- ls_zip = 'D:/temp/secret.zip'
- // 示例输入:压缩密码
- string ls_pwd
- ls_pwd = 'MyPass123'
- uo_zip lz
- lz = create uo_zip
- // bReadOnly=false 表示创建;第三个参数传密码即生成加密包
- lz.Open(ls_zip, false, ls_pwd)
- lz.Add('D:/temp/report.txt')
- lz.Close()
- destroy lz
- MessageBox('加密压缩完成', ls_zip)
复制代码
四、uo_zip 实战:解压与读取条目
前置:目标 zip 已存在(上面生成的 D:/temp/demo.zip),运行下面代码即可解压到指定目录。 - // 示例输入:要解压的 zip 文件
- string ls_zip
- ls_zip = 'D:/temp/demo.zip'
- // 示例输入:解压目标目录(不存在会自动创建)
- string ls_out
- ls_out = 'D:/temp/extract'
- uo_zip lz
- lz = create uo_zip
- lz.Open(ls_zip)
- // 解压全部条目到目标目录,返回解压出的条目数
- long ll_n
- ll_n = lz.Extract(ls_out)
- lz.Close()
- destroy lz
- MessageBox('解压完成', '共解压 ' + string(ll_n) + ' 个条目')
复制代码
读加密包时,Open 用只读 + 密码重载:lz.Open(ls_zip, true, ls_pwd)。
读取条目清单(不解压也能知道里面有什么):
- // 示例输入:要查看的 zip 文件
- string ls_zip
- ls_zip = 'D:/temp/demo.zip'
- uo_zip lz
- lz = create uo_zip
- lz.Open(ls_zip)
- // GetEntryList 通过 ref 数组回填:索引/名称/原始大小/压缩后大小/CRC/时间/是否加密
- long ll_index[]
- string ls_name[]
- long ll_size[]
- long ll_sizecom[]
- string ls_crc[]
- string ls_dt[]
- boolean lb_enc[]
- long ll_cnt
- ll_cnt = lz.GetEntryList(ll_index, ls_name, ll_size, ll_sizecom, ls_crc, ls_dt, lb_enc)
- lz.Close()
- destroy lz
- MessageBox('条目数', string(ll_cnt))
复制代码
只读取某个条目内容为 blob(常用于"从压缩包里取一张图/一份配置,不落盘"):
- // 示例输入:zip 内要读取的条目名
- string ls_name
- ls_name = 'report.txt'
- // 示例输入:zip 文件
- string ls_zip
- ls_zip = 'D:/temp/demo.zip'
- uo_zip lz
- lz = create uo_zip
- lz.Open(ls_zip)
- // 按名称读取为 blob;也可按索引:lz.ReadBlob(1)
- blob lb_data
- lb_data = lz.ReadBlob(ls_name)
- lz.Close()
- destroy lz
- MessageBox('读到的字节数', string(len(lb_data)))
复制代码
五、uo_zip 内存压缩:不落盘,直接打包 blob
有时根本没有物理文件——比如你在内存里拼好了一段 JSON、一段日志,想直接压成 zip 再通过网络发出去。用 Add(string 名称, blob 数据) 即可,无需任何物理文件:
- // 示例输入:要打包进 zip 的文本内容
- string ls_text
- ls_text = 'Hello PBIDEA,这是内存压缩示例'
- // 示例输入:zip 输出文件
- string ls_zip
- ls_zip = 'D:/temp/mem.zip'
- uo_zip lz
- lz = create uo_zip
- lz.Open(ls_zip)
- // 把字符串转 blob 后作为条目加入(条目名自定义为 memo.txt)
- blob lb_blob
- lb_blob = blob(ls_text)
- lz.Add('memo.txt', lb_blob)
- lz.Close()
- destroy lz
- MessageBox('内存压缩完成', ls_zip)
复制代码
注意:blob(字符串) 按默认编码转换,若内容含中文且需 UTF-8,请先用 toUtf8(全局函数)转好再 blob()。
六、uo_7z 完整 API 清单
uo_7z 对外方法(均来自 PbIdea.dll 的 SevenZip* 系列):
| 方法 | 说明 | | boolean AddDir(string strDir) | 加入整个目录(默认含子目录) | | boolean AddDir(string, boolean includeSubDir) | 是否含子目录 | | boolean AddDir(string, string strFilter, boolean includeSubDir) | 指定文件过滤(如 *.txt) | | boolean AddFile(string fileName) | 加入单个文件 | | boolean Compress(string str7zFile) | 开始压缩成 .7z | | boolean Compress(string str7zFile, boolean showProgress) | 压缩并显示进度条 | | boolean Extract(string str7zFile, string strDir) | 解压到目录 | | boolean Extract(string str7zFile, string strDir, boolean showProgress) | 解压并显示进度条 | | blob Compress7Z(blob data) | 内存 7z 压缩(自定义格式) | | blob Uncompress7Z(blob data) | 内存 7z 解压(须配对使用) |
和 uo_zip 不同,uo_7z 没有显式的 Open/Close:先 AddDir / AddFile 累加内容,再 Compress 一次性产出;解压则直接 Extract(源文件, 目标目录)。
七、uo_7z 压缩目录/文件为 .7z
- // 示例输入:要生成的 7z 文件
- string ls_7z
- ls_7z = 'D:/temp/demo.7z'
- // 示例输入:要压缩的目录(含子目录)
- string ls_dir
- ls_dir = 'D:/temp/sourcedir'
- // 示例输入:是否弹出进度条
- boolean lb_progress
- lb_progress = false
- uo_7z l7
- l7 = create uo_7z
- // 加入整个目录(含子目录)
- l7.AddDir(ls_dir)
- // 也可追加单个文件:l7.AddFile('D:/temp/a.txt')
- // 仅压特定类型:l7.AddDir(ls_dir, '*.txt', true)
- boolean lb_ok
- lb_ok = l7.Compress(ls_7z, lb_progress)
- destroy l7
- MessageBox('7z 压缩结果', string(lb_ok))
复制代码
八、uo_7z 解压 .7z
- // 示例输入:源 7z 文件
- string ls_7z
- ls_7z = 'D:/temp/demo.7z'
- // 示例输入:解压目标目录
- string ls_out
- ls_out = 'D:/temp/extract7z'
- uo_7z l7
- l7 = create uo_7z
- boolean lb_ok
- lb_ok = l7.Extract(ls_7z, ls_out)
- destroy l7
- MessageBox('7z 解压结果', string(lb_ok))
复制代码
九、uo_7z 内存 7z 压缩(blob,自定义格式)
Compress7Z / Uncompress7Z 走的是 7z 内核的自定义内存格式,适合把一段二进制数据压小后存库或走网络,但只能用 Uncompress7Z 还原,标准 7z 工具打不开这段 blob——这点务必记牢。
- // 示例输入:要压缩的二进制数据(这里用字符串转 blob 示意)
- blob lb_src
- lb_src = blob('PBIDEA 7z 内存压缩示例内容')
- // 若是读真实文件:lb_src = lz.ReadFile('D:/temp/big.dat')
- uo_7z l7
- l7 = create uo_7z
- // 压缩
- blob lb_cmp
- lb_cmp = l7.Compress7Z(lb_src)
- // 解压还原(必须配对使用)
- blob lb_dec
- lb_dec = l7.Uncompress7Z(lb_cmp)
- destroy l7
- MessageBox('压缩前后字节数', string(len(lb_src)) + ' -> ' + string(len(lb_cmp)))
复制代码
十、进阶:配合 uo_dir 做选择性遍历再压缩
uo_zip.AddPath 是"整目录无脑打包"。如果你想按条件挑选文件(比如只压最近修改的、或只压某扩展名),可以先用 uo_dir(它是 autoinstantiate,无需 create)枚举文件清单,再逐一对 uo_zip 的 Add 调用。
- // 示例输入:要遍历的目录
- string ls_dir
- ls_dir = 'D:/temp/sourcedir'
- // 示例输入:文件名过滤(* 表示全部)
- string ls_filter
- ls_filter = '*.txt'
- // 示例输入:zip 输出路径
- string ls_zip
- ls_zip = 'D:/temp/selected.zip'
- uo_dir ld
- // uo_dir 为 autoinstantiate,无需 create
- string ls_files[]
- long ll_cnt
- ll_cnt = ld.files(ls_dir, ls_filter, ls_files)
- uo_zip lz
- lz = create uo_zip
- lz.Open(ls_zip)
- long i
- for i = 1 to ll_cnt
- // 对每个命中的文件加入压缩包(这里直接用其完整路径作为条目名)
- lz.Add(ls_files[i])
- next
- lz.Close()
- destroy lz
- MessageBox('已挑选并压缩的文件数', string(ll_cnt))
复制代码
uo_dir 常用配套:md(dir) 建多级目录、exist(file) 判断存在、dirs(dir, ref) 列子目录、del(file) 删文件、GetSize(dir) 算目录大小,均可直接调用。
边界情况与常见坑
- 忘了 Close() 导致文件没生成:uo_zip 的所有 Add* 都只是"暂存",只有 Close() 才真正写盘;出错想放弃用 Discard()。这是最高频的坑。
- uo_zip / uo_7z 必须 create/destroy:两者 constructor/destructor 里分别调用了 zipCreate/zipDestroy 与 SevenZipCreate/SevenZipDestroy,不 create 会报空对象,不 destroy 会泄漏底层句柄。
- 7z 内存压缩的 blob 不能用标准工具解:Compress7Z 产出的是自定义格式,归档给别人的 7z 请用 Compress(文件) 走标准 .7z;内存 blob 仅限自己 Uncompress7Z 还原。
- GetEntryList 的 ref 数组要预先声明:七个 ref 数组(index/name/size/sizecom/crc/dt/enc)都要先声明再传入,函数通过引用回填,返回值是条目总数。
- 中文路径/密码:底层走系统 API,建议路径与密码尽量使用 ASCII;中文文件名一般可行,但跨机器交换时优先用英文文件名最稳妥。
- Open(blob, 密码) 只读打开:这个重载只能读不能写,用来"从一段内存里的 zip 数据直接读取条目"很方便,但别指望再往里 Add。
- PbIdea.dll 必须随应用分发:这两个对象都依赖 PbIdea.dll,开发机能跑不代表客户机能跑,部署时务必把 dll 一起带上。
ZIP 还是 7z?以及替代方案
- 兼容性优先 → uo_zip(ZIP):ZIP 是事实标准,Windows 资源管理器、各类解压软件原生支持,客户拿到就能双击打开,适合对外分发的附件、更新包。
- 压缩率优先 → uo_7z(7z):7z 对大量小文件、文本类数据通常压得更小,适合内部归档、备份传输;但对方需要 7-Zip 或支持 7z 的工具才能解。
- 纯内存数据压小 → 两者都行:uo_zip 的 Add(名称, blob) + 不解压直接用 ReadBlob;uo_7z 的 Compress7Z/Uncompress7Z(注意自定义格式)。
- 替代方案:若只要最简单且不想依赖 PbIdea.dll,可用 Run 调用系统/第三方的 7z.exe、tar.exe;但那样要处理命令行、等待进程、解析返回,远不如这两个对象来得干净、可控。PBIDEA 没有内置 rar 对象,需要 rar 格式时仍建议走外部 exe 或改用 zip/7z。
小结
在 PB 里做压缩解压,记住三件事就够用了:uo_zip 管 ZIP(Open→Add→Close 三段式,别忘了 Close)、uo_7z 管 7z(AddDir/AddFile→Compress,Extract 直接解)、两者都靠 PbIdea.dll 且都要 create/destroy。需要挑文件就用 uo_dir 先枚举再 Add。把这几段代码收进你的工具库,以后导出、归档、打包上传都能直接复用。 |