utl_smtp是发送html邮件和附件的唯一可行路径,因其支持starttls/ssl、自定义认证及手动构造mime多部分结构;而utl_mail仅支持纯文本、无加密、不兼容现代smtp服务器,且在587端口静默失败。

直接说结论:UTL_SMTP 本身不内置 HTML 或附件支持,必须手动构造 MIME 多部分结构,且中文、换行、编码、边界符稍有错位,收件人就看到空白邮件或乱码附件。
为什么用 UTL_SMTP.send 而不用 UTL_MAIL
UTL_MAIL 只支持纯文本、无认证、不兼容 STARTTLS/SSL,连 Gmail 或腾讯企业邮都发不出去;而 UTL_SMTP 虽然要手写协议头,但能控制认证方式、端口、加密握手和 MIME 结构——这是发 HTML+附件的唯一可行路径。
- UTL_MAIL.SEND 在端口 587 上会静默失败,因为它不发
STARTTLS命令 - UTL_SMTP 允许你调用
UTL_SMTP.START_TLS(Oracle 11gR2+),或用UTL_TCP.CRLF手动协商 SSL 连接 - HTML 和附件必须共存于同一封
multipart/mixed邮件中,UTL_MAIL 根本不提供write_raw_data接口来写二进制附件流
HTML 邮件必须写对 MIME 头和空行分隔
哪怕只漏一个 CRLF 或多一个空格,Outlook 就当纯文本渲染,Gmail 直接丢弃 HTML 段。
- 整个邮件体必须以
MIME-Version: 1.0开头,紧接着Content-Type: multipart/alternative; boundary="xxx" - HTML 和纯文本内容必须用
--xxx分隔,且每个段落头尾都要有CRLF,段与段之间**必须空一行**(即两个CRLF) - HTML 段的
Content-Type必须写成text/html; charset=UTF-8,不能写charset=GBK—— 否则微信邮箱、iOS Mail 会乱码 - 别用
UTL_SMTP.write_data写 HTML 字符串:它会把中文转成问号;改用UTL_SMTP.write_raw_data(conn, UTL_RAW.cast_to_raw(CONVERT(p_html, 'AL32UTF8')))
附件名含中文时 filename*=UTF-8''xxx 是硬性要求
只写 filename="报告.xlsx",在 Outlook、Apple Mail、Foxmail 里附件名全变成 ATT00001.dat 或乱码;filename*=UTF-8''%E6%8A%A5%E5%91%8A.xlsx 才是标准解法。
- 附件数据本身要用
UTL_ENCODE.BASE64_ENCODE编码,再按 76 字符每行切分(否则某些网关会截断) - 附件头必须包含:
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet+Content-Transfer-Encoding: base64+Content-Disposition: attachment; filename*=UTF-8''%E6%8A%A5%E5%91%8A.xlsx - 注意两个单引号
''是 RFC 5987 强制格式,少一个就会失效;不要用filename*=UTF-8''报告.xlsx(未编码)—— 客户端不认 - 如果附件来自 BLOB,先用
DBMS_LOB.READ分块读取,每次最多 32767 字节,再逐块 base64 编码写入
最常被忽略的底层依赖:ACL、字符集、网络出口策略
代码写得再对,只要这三项没配好,UTL_SMTP.OPEN_CONNECTION 就直接报 ORA-24247 或超时,连登录环节都进不去。
- 数据库用户必须有
EXECUTE权限:对UTL_SMTP、UTL_TCP、UTL_ENCODE、UTL_RAW - 必须创建 ACL 并绑定 SMTP 主机和端口:
DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL的host参数要填域名(如smtp.exmail.qq.com),不能填 IP;lower_port/upper_port必须覆盖实际使用的端口(465 或 587) - 数据库服务器 OS 层必须能
telnet smtp.exmail.qq.com 587通——不是开发机,不是应用服务器,就是 DB Server 本机。很多企业禁了 outbound,得走内部 relay(如mail-relay.internal:25) - 如果数据库字符集是
ZHS16GBK,所有CONVERT(..., 'AL32UTF8')调用前,需确认源字符串已用UTL_RAW.CAST_TO_VARCHAR2正确转出,否则CONVERT会静默失败
真正卡住人的从来不是怎么写 MIME,而是 ACL 没绑对主机、端口填成 25 却连 587 的 STARTTLS、附件名漏了 filename*= 的星号和双单引号——这些地方一错,整封邮件就“发出去了但收不到”,排查成本远高于写代码本身。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











