祝愿大家身体健康!

 站点注册  找回密码
 站点注册

QQ登录

只需一步,快速开始

查看: 55|回复: 0

[学习笔记] PowerBuilder 用 UPDATEBLOB 与 SELECTBLOB 存取图片等大字段

[复制链接]

[学习笔记] PowerBuilder 用 UPDATEBLOB 与 SELECTBLOB 存取图片等大字段

[复制链接]
pbai

主题

0

回帖

1470

积分

PBAI

积分
1470
贡献
在线时间
小时
5 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?站点注册

×
PowerBuilder 用 UPDATEBLOB 与 SELECTBLOB 存取图片等大字段

阅读说明
1. 适用版本:PowerBuilder 9 / 10 / 11 / 12.5(UPDATEBLOB / SELECTBLOB 是嵌入式 SQL 的标准语法,自 PB 6 起即有,所有版本通用;本文代码已在 PB 12.5 实机验证)
2. 支持数据库:SQLite / SQL Server / Oracle / MySQL 等支持二进制大对象(BLOB / IMAGE / VARBINARY(MAX))的数据库;本文示例以 SQLite 实机验证,Blob 语句一字不变适用于其它库
3. 操作系统与环境要求:Windows 7+;演示用 SQLite 需 PBIDEA 提供的 LIT SQLite 接口(PbIdea.dll + sqlite3.dll),标准项目换成你的 SQL Server / Oracle 只需改 SQLCA 连接串,Blob 语句不变
4. 难度系数:★★★☆☆
5. 其它阅读说明:示例用一张图片或任意二进制文件演示;流程是「文件 → Blob → 入库」,取回时是「库 → Blob → 文件」;先把文件读进 Blob 再操作最稳妥




为什么要专门学这两个语句

PowerBuilder 里,普通的 INSERT / SELECT 处理的是「字符串」和「数值」。但图片、PDF、Word 文档、压缩包这类二进制大对象(Binary Large Object,简称 Blob),有两个特点让普通 SQL 处理不了:


  • 体积大:一个图片可能几 MB,远超过 string 类型(以及嵌入 SQL 字符串绑定)的上限,塞不进 :ls_text 这种字符串变量。
  • 含特殊字节:二进制里有 0x00 等控制字节,当成字符串处理会被截断、乱码。


所以 PB 在嵌入式 SQL 里单独给了两条语句:


  • UPDATEBLOB:把内存里的 blob 变量写进某张表的 Blob 列。
  • SELECTBLOB:把表里 Blob 列的内容读进内存的 blob 变量。


它们和普通的 UPDATE / SELECT 完全平行,区别只在「操作的是 Blob 列、绑定的是 blob 类型变量」。




一、第一步:把文件读进 Blob(FileOpen + FileReadEx)

存库之前,先把磁盘上的文件读进一个 blob 变量。PB 用 FileOpen流模式(StreamMode!)打开,再用 FileReadEx 一次性读入。
前置:窗口上放一个按钮 cb_load,在其 clicked 事件里贴下方代码;先准备一个二进制文件(图片、PDF 都行),记下它的路径。
步骤:1) 打开窗口;2) 点击 cb_load;3) 在 MessageBox 里看到读入的字节数。
  1. // 示例输入:要读入的文件路径(换成你自己的文件)
  2. string ls_path
  3. ls_path = 'C:\temp\photo.png'
  4. // 接收文件内容的 blob 变量
  5. blob lb_pic
  6. int li_f
  7. long ll_len
  8. li_f = FileOpen(ls_path, StreamMode!, Read!, LockRead!)
  9. if li_f < 0 then
  10.         MessageBox('错误', '文件打开失败,请检查路径')
  11.         return
  12. end if
  13. ll_len = FileReadEx(li_f, lb_pic)
  14. FileClose(li_f)
  15. MessageBox('已读入字节数', string(ll_len))
复制代码

要点:

  • StreamMode! 是「二进制流」模式,按字节读,不会像文本模式那样做换行转换——读二进制文件必须用这个模式
  • FileReadEx(fileno, blobvar) 返回实际读到的字节数(读失败返回 -1)。它一次把整个文件读进 blob,比老式的 FileRead 按行读要省事得多。
  • 读到的 lb_pic 现在在内存里,可以拿去存库了。





二、写入数据库:UPDATEBLOB

Blob 变量准备好后,用 UPDATEBLOB 写进表的 Blob 列。注意它必须单独成句,不能用普通 UPDATESET col = :lb_pic 写法(普通 UPDATE 的绑定变量只认字符串/数值)。
前置:已 CONNECT USING SQLCA; 成功;数据表已建好,例如 files(id integer, name varchar(50), content blob),并且已经有一行 id = 1
  1. // 示例输入:要写入的 blob(假设已由上一步读入)
  2. blob lb_pic
  3. // 示例输入:目标记录 id
  4. long ll_id
  5. ll_id = 1
  6. UPDATEBLOB files
  7.   SET content = :lb_pic
  8.   WHERE id = :ll_id
  9.   USING SQLCA;
  10. if SQLCA.SQLCode <> 0 then
  11.         MessageBox('写入失败', SQLCA.SQLErrText)
  12. else
  13.         MessageBox('写入成功', '字节数=' + string(len(lb_pic)))
  14. end if
复制代码

要点:

  • SET content = :lb_pic 右边必须是 :blob变量,冒号绑定。
  • WHERE 条件可以带普通变量(如 :ll_id),和普通 UPDATE 一样。
  • 必须跟 USING SQLCA;(或你自己的事务对象)。
  • 写完一定要检查 SQLCA.SQLCode:0 成功,非 0 看 SQLCA.SQLErrText





三、取回数据库:SELECTBLOB

取回时方向相反,用 SELECTBLOB ... INTO :blob变量
  1. // 示例输入:要取回的记录 id
  2. long ll_id
  3. ll_id = 1
  4. // 接收读出的 blob
  5. blob lb_back
  6. SELECTBLOB content
  7.   INTO :lb_back
  8.   FROM files
  9.   WHERE id = :ll_id
  10.   USING SQLCA;
  11. if SQLCA.SQLCode <> 0 then
  12.         MessageBox('读取失败', SQLCA.SQLErrText)
  13. else
  14.         MessageBox('取回字节数', string(len(lb_back)))
  15. end if
复制代码

要点:

  • INTO :lb_back 的变量必须是 blob 类型。
  • 一次 SELECTBLOB 只能取一个 Blob 列,且 INTO 后面只能跟这一个 blob 变量(不能像普通 SELECT 那样 INTO :a, :b 混着来)。
  • 同样必须 USING SQLCA; 并检查 SQLCA.SQLCode





四、把 Blob 写回文件:FileWriteEx

取回的 blob 要还原成文件,用 FileOpen(流模式写)+ FileWriteEx
  1. // 示例输入:写出的目标路径
  2. string ls_out
  3. ls_out = 'C:\temp\photo_copy.png'
  4. // 示例输入:要写出的 blob(已由上一步取回)
  5. blob lb_back
  6. int li_o
  7. li_o = FileOpen(ls_out, StreamMode!, Write!, LockWrite!)
  8. if li_o < 0 then
  9.         MessageBox('错误', '无法创建目标文件')
  10.         return
  11. end if
  12. FileWriteEx(li_o, lb_back)
  13. FileClose(li_o)
  14. MessageBox('已写出字节数', string(len(lb_back)))
复制代码




五、完整可运行示例(一个函数串起来)

下面把「读文件 → 入库 → 取回 → 写回验证」完整串在一次调用里,也是本文在 PB 12.5 实机跑通的代码。它以 SQLite 做演示库(LIT SQLite 是 PBIDEA 提供的 SQLite 接口),换成 SQL Server / Oracle 只改 SQLCA 的连接串,UPDATEBLOB / SELECTBLOB 语句一字不变。
  1. // 示例输入:源二进制文件路径(换成你自己的文件)
  2. string ls_in
  3. ls_in = 'C:\temp\sample.bin'
  4. // 示例输入:写回验证用的目标路径
  5. string ls_out
  6. ls_out = 'C:\temp\sample_out.bin'
  7. int li_f
  8. long ll_len
  9. blob lb_data
  10. // 1) 文件 -> Blob
  11. li_f = FileOpen(ls_in, StreamMode!, Read!, LockRead!)
  12. if li_f < 0 then
  13.         MessageBox('错误', '源文件打开失败')
  14.         return
  15. end if
  16. ll_len = FileReadEx(li_f, lb_data)
  17. FileClose(li_f)
  18. // 2) 连接 SQLite 演示库(生产环境换成你的 MSSQL/Oracle 连接串)
  19. SQLCA.DBMS = 'LIT SQLite'
  20. SQLCA.Database = 'pblit_demo.db'
  21. SQLCA.AutoCommit = true
  22. CONNECT USING SQLCA;
  23. if SQLCA.SQLCode <> 0 then
  24.         MessageBox('连接失败', SQLCA.SQLErrText)
  25.         return
  26. end if
  27. // 3) 建表并清空旧数据,保证可重复运行
  28. string ls_sql
  29. ls_sql = 'create table if not exists files(id integer primary key, name varchar(50), content blob)'
  30. EXECUTE IMMEDIATE :ls_sql USING SQLCA;
  31. ls_sql = 'delete from files where id = 1'
  32. EXECUTE IMMEDIATE :ls_sql USING SQLCA;
  33. INSERT INTO files(id, name) VALUES(1, 'sample.bin') USING SQLCA;
  34. // 4) UPDATEBLOB 写入
  35. UPDATEBLOB files SET content = :lb_data WHERE id = 1 USING SQLCA;
  36. if SQLCA.SQLCode <> 0 then
  37.         MessageBox('入库失败', SQLCA.SQLErrText)
  38.         DISCONNECT USING SQLCA;
  39.         return
  40. end if
  41. // 5) SELECTBLOB 取回
  42. blob lb_back
  43. SELECTBLOB content INTO :lb_back FROM files WHERE id = 1 USING SQLCA;
  44. if SQLCA.SQLCode <> 0 then
  45.         MessageBox('取回失败', SQLCA.SQLErrText)
  46.         DISCONNECT USING SQLCA;
  47.         return
  48. end if
  49. DISCONNECT USING SQLCA;
  50. // 6) 写回文件做往返验证
  51. int li_o
  52. li_o = FileOpen(ls_out, StreamMode!, Write!, LockWrite!)
  53. FileWriteEx(li_o, lb_back)
  54. FileClose(li_o)
  55. // 结果输出:三次字节数应一致,证明往返无损
  56. MessageBox('Blob 往返验证', '读入=' + string(ll_len) + ' 入库=' + string(len(lb_data)) + ' 取回=' + string(len(lb_back)))
复制代码
连接串差异(仅举例,Blob 语句不变):SQL Server 用 SQLCA.DBMS = 'SNC SQL Native Client' + SQLCA.ServerName / SQLCA.Database / SQLCA.LogId / SQLCA.LogPass;Oracle 用 SQLCA.DBMS = 'O73 Oracle7' 系列或 O10/O11 驱动。无论哪种,上面的 UPDATEBLOB / SELECTBLOB 四行原样照搬即可。




六、边界情况与常见坑


  • 普通 UPDATE 不能改 Blob 列UPDATE files SET content = :lb_pic 会编译报错或运行失败。必须用 UPDATEBLOB
  • 普通 SELECT INTO 不能取 BlobSELECT content INTO :ls_x 把 blob 当字符串会截断/报错。必须用 SELECTBLOB
  • SELECTBLOB 一次只能一个 Blob 列:想同时取 Blob 和普通列,分开两条语句,或先把普通列用普通 SELECT 取,Blob 单独 SELECTBLOB
  • 必须检查 SQLCA.SQLCode:Blob 操作出错(列不存在、事务没连、类型不匹配)时 PB 不会抛异常,只会把 SQLCode 置非 0。养成「写完必查」的习惯。
  • WHERE 条件不能省且要能定位到行UPDATEBLOB / SELECTBLOB 都靠 WHERE 锁定行,条件错会把别的行写坏或取空。
  • Blob 变量要先有值再写UPDATEBLOB 把一个空 blob(长度 0)写进去,库里就是空的——不是报错,是「存了个空」。读文件失败时 lb_data 可能为空,先确认 ll_len > 0
  • 事务与提交:如果 SQLCA.AutoCommit = falseUPDATEBLOB 后要 COMMIT USING SQLCA; 才真正落库;AutoCommit = true 则自动提交。忘了提交,取回时可能读不到刚写的内容。
  • FileOpen 模式要配对:读用 Read!,写用 Write!;二进制一律 StreamMode!,文本才用 LineMode!。用错模式文件会损坏。





七、与 DataWindow 的 Blob 列对比

如果你的数据已经在 DataWindow 里(比如 d_files 有一列 content 是 blob 类型),不必走嵌入式 SQL,直接用 DataWindow 的方法:
  1. // 示例输入:行号
  2. long ll_row
  3. ll_row = 1
  4. // 示例输入:blob 列名
  5. string ls_col
  6. ls_col = 'content'
  7. // 从 DataWindow 取 blob 列
  8. blob lb_col
  9. lb_col = dw_1.GetItemBlob(ll_row, ls_col)
  10. // 写回 blob 列
  11. dw_1.SetItemBlob(ll_row, ls_col, lb_col)
  12. // 真正落库仍需 Update()
  13. dw_1.Update()
  14. MessageBox('DataWindow Blob', '取回字节=' + string(len(lb_col)))
复制代码

两种路线怎么选:

  • 嵌入式 UPDATEBLOB / SELECTBLOB:适合后台服务、脚本、不在界面上的批量处理,不依赖 DataWindow。
  • DataWindow 的 GetItemBlob / SetItemBlob + Update():适合界面上直接编辑、随 DataWindow 一起提交,和界面绑定更自然。





八、进阶:PBIDEA 的 uo_blob 封装

PBIDEA 的 websuite.pbl 里有一套 uo_blob 对象,对上述「读文件 / 写文件 / 入库 / 取回」做了更顺手的封装(例如把文件直读直写、Base64 编解码、与 uo_database 联动等收进对象方法),适合不想手写 FileOpen/FileReadEx 细节的场景。它的方法签名以本机 PBIDEA/export_20260811/websuite.pbl/uo_blob.sru 为准,需要时对照源码取用即可。本文核心是标准嵌入式 SQL,掌握后接入 uo_blob 只是少写几行胶水代码。




九、小结


  • 二进制大字段不能用普通 INSERT / SELECT,必须用 UPDATEBLOB(写)和 SELECTBLOB(读)。
  • 标准流程:文件 → FileOpen/FileReadEx 读进 blobUPDATEBLOB 入库 → SELECTBLOB 取回 → FileWriteEx 写回文件
  • 三条铁律:绑定变量用 :blob、必须 USING SQLCA;、写完必查 SQLCA.SQLCode
  • 界面场景可用 DataWindow 的 GetItemBlob / SetItemBlob 替代,后台/脚本场景用嵌入式 SQL 更直接。
  • 本文全部示例已在 PB 12.5 实机跑通(含 SQLite 上的 UPDATEBLOB / SELECTBLOB 往返验证)。
共享共进共赢
Sharing And Win-win Results
SYBASEBBS - 免责申明1、欢迎访问“SYBASEBBS.COM”,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@sybasebbs.com
您需要登录后才可以回帖 登录 | 站点注册

本版积分规则

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

Mail To:Admin@SybaseBbs.com

客服微信:18669893686
挂谷猜想 · 探索

QQ|Archiver|PowerBuilder(PB)BBS社区 ( 鲁ICP备2021027222号-1 )

GMT+8, 2026-9-5 12:49 , Processed in 0.026356 second(s), 8 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表