UTL_MAIL不支持附件,因其无附件参数且mime_type仅控制正文类型;必须用UTL_SMTP手动构造MIME多部分结构,严格设置boundary、头字段及BLOB分块≤57字节。
UTL_MAIL 本身不支持附件,硬要用它发带附件的邮件会直接报错 ORA-29261: bad argument。必须切换到 UTL_SMTP + 手动构造 MIME 多部分结构。
为什么不能用 UTL_MAIL.SEND 发附件
utl_mail 是 oracle 10g 起提供的简化封装,只暴露了 sender、recipients、message 等基础参数,压根没留附件入口。它的 mime_type 参数仅控制正文类型(如 text/html),无法触发 multipart boundary 或嵌入附件段。
常见误操作是试图把附件内容拼进 message 参数里——这只会让收件方看到一堆乱码 Base64,且 Outlook/Gmail 无法识别为可下载附件。
UTL_SMTP 发附件必须手动写 MIME 边界和头字段
MIME 邮件本质是文本协议:用唯一分隔符(boundary)切开正文、附件等不同部分,每部分带自己的 Content-Type、Content-Disposition 和编码声明。Oracle 不提供现成的“加附件”函数,全靠手写 utl_smtp.write_data 拼接。
- 边界字符串必须全局唯一,推荐用
raw类型的sys_guid(),避免和正文内容冲突 - 附件头必须包含三行:
Content-Type(如application/vnd.ms-excel)、Content-Disposition: attachment; filename="xxx.xlsx"、Content-Transfer-Encoding: base64 - 正文部分要放在第一个 boundary 之后、附件之前,且结尾要有空行(
utl_tcp.crlf) - 整个邮件末尾必须以
--boundary--结束,否则 Gmail 会拒收
发送 BLOB 附件时,dbms_lob.read 分块大小不能超 57 字节
Base64 编码要求输入长度是 3 的倍数,而 utl_encode.base64_encode 对 RAW 输入有隐式限制:若单次传入超过 57 字节的 raw,会截断或报 ORA-29275: partial multibyte character。
- 固定用
l_buff_size := 57,这是经 Oracle 官方文档和大量生产环境验证的安全值 - 别用 60、64 这类“看起来整除”的数字——Base64 编码后每 57 字节原始数据生成 76 字符,刚好填满 SMTP 行宽限制
- 循环读取时,
l_idx要从 1 开始(LOB 下标起始为 1),且dbms_lob.read的第三个参数是偏移量,不是索引
文件路径和权限常被忽略的两个硬性前提
如果附件来自数据库外的文件(比如 CSV),你得先用 UTL_FILE 读取,而这依赖操作系统目录对象和权限。
- 必须存在可用的
DIRECTORY对象(如DATA_PUMP_DIR),且当前用户有READ权限:GRANT READ ON DIRECTORY DATA_PUMP_DIR TO your_user; -
UTL_FILE.FOPEN的路径参数只能是该 DIRECTORY 对象映射的物理路径,不能写绝对路径如/tmp/report.csv,否则报ORA-29280: invalid directory path - 若附件是动态生成的 BLOB(如导出查询结果),跳过文件系统,但需确保
dbms_lob.createtemporary分配足够内存,否则大附件会触发ORA-04030
UTL_SMTP 替代 UTL_MAIL、严格按 MIME 规范写 boundary 和头字段、BLOB 分块死守 57 字节。少一个,邮件就发不出去,或者附件打不开。


















