最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如何在Oracle PL/SQL中发送邮件?
时间:2026-08-06 07:47:48 编辑:袖梨 来源:一聚教程网
Oracle 12c+中DBMS_MAIL已彻底删除,必须改用UTL_MAIL或UTL_SMTP;启用UTL_MAIL需三步:手动安装utlmail.sql与prvtmail.plb脚本、显式设置带端口的smtp_out_server参数、确保DB服务器直连SMTP端口且配置ACL权限。
Oracle 12c 及更高版本中 DBMS_MAIL 已被彻底删除,调用必报 ORA-00904: "dbms_mail": invalid identifier ——这不是权限或安装问题,是包本身不存在了。必须改用 UTL_MAIL 或 UTL_SMTP。
UTL_MAIL 启用失败的三个硬性前提
看起来一行 UTL_MAIL.SEND 就能发信,但 80% 的失败卡在这三步没走完:
- DBA 必须手动执行安装脚本:
@?/rdbms/admin/utlmail.sql和@?/rdbms/admin/prvtmail.plb(路径中?展开为$ORACLE_HOME) -
smtp_out_server参数必须设且带端口:ALTER SYSTEM SET smtp_out_server = 'smtp.example.com:587' SCOPE=BOTH;——不能写http://,也不能漏端口 - 数据库服务器自身需能直连邮件服务器:防火墙、SELinux、网络策略必须放行出站
587或465端口(不是应用服务器能连就行)
验证是否生效:SELECT value FROM v$parameter WHERE name = 'smtp_out_server'; 返回非空字符串才算通过。
UTL_MAIL.SEND 中文乱码与长度陷阱
主题或正文变问号?不是字符集没配对,就是超长触发误导性错误:
- 必须显式指定
mime_type => 'text/plain; charset=utf-8'(HTML 邮件同理),否则默认用数据库字符集(如WE8ISO8859P1),中文直接报废 -
subject和message参数上限为 32767 字节 —— 超长会抛ORA-29260: network error: TNS:connection refused,实际和网络无关,纯长度校验失败 - 多个收件人必须拼成单字符串:
'[email protected],[email protected]',不能传数组、集合或换行符
最小可用示例:
BEGINUTL_MAIL.SEND(sender => '[email protected]',recipients => '[email protected]',subject=> '测试',message=> '这是一封测试邮件',mime_type=> 'text/plain; charset=utf-8');END;
UTL_SMTP 连接卡死在 STARTTLS 的关键动作
当邮件服务器强制要求 STARTTLS(如 Gmail、Outlook、多数企业 SMTP),UTL_SMTP.OPEN_CONNECTION 后直接发 MAIL 会挂起或报 ORA-29279:
- 必须先发
EHLO(不是HELO),再发STARTTLS命令 - 发完
STARTTLS后,需重新协商 TLS 层 —— Oracle 不自动处理,得手动调用UTL_SMTP.STARTTLS(12.2+)或用UTL_TCP手动包装 SSL 握手(旧版本) - 认证用户名/密码必须 Base64 编码:
UTL_ENCODE.base64_encode(UTL_RAW.cast_to_raw(v_user)),漏掉任意一环都会 535 错误 - 邮件头与正文之间必须有且仅有一个空行(
UTL_TCP.CRLF || UTL_TCP.CRLF),少或多都会被拒
中文支持依赖字符集转换:UTL_RAW.cast_to_raw(CONVERT(v_msg, 'ZHS16GBK', 'AL32UTF8')),不能只靠 mime_type 声明。
ACL 权限不是可选项,而是前置开关
即使 UTL_MAIL 或 UTL_SMTP 配置全对,没 ACL 就是白搭 —— 报错 ORA-24247: network access denied by access control list:
- ACL 文件名、主机名、端口范围必须严格匹配:
DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL(acl => 'email.xml', host => '*', lower_port => 25, upper_port => 587) - 用户必须被明确授予
connect权限:DBMS_NETWORK_ACL_ADMIN.ADD_PRIVILEGE('email.xml', 'MY_USER', TRUE, 'connect') - ACL 生效后,仍需显式授权包执行权:
GRANT EXECUTE ON UTL_MAIL TO MY_USER(UTL_SMTP同理)
真正容易被忽略的是:ACL 主机匹配支持通配符但不支持正则;host => '*' 允许所有域名/IP,但若只配了 smtp.example.com,连 smtp.example.com:587 都会被拒绝 —— 端口必须单独放开。