You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:使用UTL_SMTP发送服务器目录中Excel附件的存储过程问题

PL/SQL 邮件存储过程修正(支持HTML正文+Excel附件)

原代码存在多处逻辑缺失与错误,导致无法正常发送带HTML正文和Excel附件的邮件,以下是修正后的完整存储过程:

CREATE OR REPLACE PROCEDURE p_send_email_with_attach AS
    file_handle      utl_file.file_type;
    output           VARCHAR2(32767);
    attachment_text  CLOB; -- 改用CLOB避免VARCHAR2长度限制
    v_dir            VARCHAR2(300) := 'RPT_DIR';
    v_recipients     VARCHAR2(2000) := 'abc@gmail.com';
    v_sub            VARCHAR2(300) := 'Hello';
    v_smtp_server    VARCHAR2(255) := 'mailhost';
    conn             utl_smtp.connection;
    c_mime_boundary  CONSTANT VARCHAR2(256) := '----=_Part_' || TO_CHAR(SYSTIMESTAMP, 'YYYYMMDDHH24MISSFF');
    v_from_name      VARCHAR2(100) := 'xyz@yahoo.com';
    -- HTML正文示例(多表格结构)
    v_html_body      VARCHAR2(32767) := '<html>
<head><title>邮件正文</title></head>
<body>
<h3>报表数据</h3>
<table border="1">
    <tr><th>ID</th><th>名称</th></tr>
    <tr><td>1</td><td>测试数据1</td></tr>
    <tr><td>2</td><td>测试数据2</td></tr>
</table>
<br>
<table border="1">
    <tr><th>月份</th><th>销售额</th></tr>
    <tr><td>1月</td><td>10000</td></tr>
    <tr><td>2月</td><td>12000</td></tr>
</table>
</body>
</html>';
BEGIN
    -- 1. 读取Excel/CSV文件内容到CLOB
    file_handle := utl_file.fopen(location => v_dir, filename => 'My_First_CSV.CSV', open_mode => 'R');
    attachment_text := EMPTY_CLOB();
    LOOP
        BEGIN
            utl_file.get_line(file_handle, output);
            -- 将读取到的行追加到CLOB,添加换行符
            attachment_text := attachment_text || output || utl_tcp.crlf;
        EXCEPTION
            WHEN no_data_found THEN
                EXIT;
        END;
    END LOOP;
    utl_file.fclose(file_handle);

    -- 2. 建立SMTP连接
    conn := utl_smtp.open_connection(v_smtp_server);
    utl_smtp.helo(conn, v_smtp_server);
    utl_smtp.mail(conn, v_from_name);

    -- 处理收件人(拆分分号分隔的收件人列表)
    FOR cur_r IN (
        SELECT regexp_substr(v_recipients, '[^;]+', 1, level) AS v_recipient
        FROM dual
        CONNECT BY regexp_substr(v_recipients, '[^;]+', 1, level) IS NOT NULL
    ) LOOP
        utl_smtp.rcpt(conn, cur_r.v_recipient);
    END LOOP;

    -- 3. 开始写入邮件数据
    utl_smtp.open_data(conn);

    -- 邮件头信息
    utl_smtp.write_data(conn, 'From: ' || v_from_name || utl_tcp.crlf);
    utl_smtp.write_data(conn, 'To: ' || v_recipients || utl_tcp.crlf);
    utl_smtp.write_data(conn, 'Subject: ' || v_sub || utl_tcp.crlf);
    -- 声明MIME多部分邮件
    utl_smtp.write_data(conn, 'MIME-Version: 1.0' || utl_tcp.crlf);
    utl_smtp.write_data(conn, 'Content-Type: multipart/mixed; boundary="' || c_mime_boundary || '"' || utl_tcp.crlf || utl_tcp.crlf);

    -- 4. 添加HTML正文部分
    utl_smtp.write_data(conn, '--' || c_mime_boundary || utl_tcp.crlf);
    utl_smtp.write_data(conn, 'Content-Type: text/html; charset=UTF-8' || utl_tcp.crlf);
    utl_smtp.write_data(conn, 'Content-Disposition: inline' || utl_tcp.crlf || utl_tcp.crlf);
    utl_smtp.write_data(conn, v_html_body || utl_tcp.crlf || utl_tcp.crlf);

    -- 5. 添加Excel附件部分(Base64编码)
    utl_smtp.write_data(conn, '--' || c_mime_boundary || utl_tcp.crlf);
    utl_smtp.write_data(conn, 'Content-Type: application/vnd.ms-excel; name="My_First_CSV.CSV"' || utl_tcp.crlf);
    utl_smtp.write_data(conn, 'Content-Disposition: attachment; filename="My_First_CSV.CSV"' || utl_tcp.crlf);
    utl_smtp.write_data(conn, 'Content-Transfer-Encoding: base64' || utl_tcp.crlf || utl_tcp.crlf);
    -- 将CLOB内容Base64编码后分段写入(避免单行长限制)
    utl_smtp.write_data(conn, utl_encode.base64_encode(utl_raw.cast_to_raw(attachment_text)) || utl_tcp.crlf || utl_tcp.crlf);

    -- 6. 结束MIME边界
    utl_smtp.write_data(conn, '--' || c_mime_boundary || '--' || utl_tcp.crlf);

    -- 关闭数据写入与连接
    utl_smtp.close_data(conn);
    utl_smtp.quit(conn);

EXCEPTION
    WHEN OTHERS THEN
        -- 异常时关闭未释放的资源
        IF utl_file.is_open(file_handle) THEN
            utl_file.fclose(file_handle);
        END IF;
        IF utl_smtp.is_connected(conn) THEN
            utl_smtp.quit(conn);
        END IF;
        raise_application_error(-20001, '发送邮件失败: ' || SQLERRM);
END p_send_email_with_attach;
/

关键修正点说明:

  • 文件读取逻辑:改用CLOB存储文件内容,避免VARCHAR2的长度限制;修复行内容追加逻辑,将读取到的output实际加入到变量中。
  • MIME结构完善:补充邮件头(From/To/Subject)、MIME版本声明,明确多部分邮件的边界规则。
  • HTML正文支持:添加独立的HTML正文部分,设置Content-Disposition: inline使其在邮件中直接显示,并指定UTF-8编码避免乱码。
  • 附件处理:将附件类型修正为application/vnd.ms-excel,使用base64编码处理文件内容(符合SMTP传输规范),避免原始文本传输导致格式损坏。
  • 收件人处理:改为拆分v_recipients变量中的分号分隔列表,无需依赖额外表,逻辑更通用。
  • 异常资源释放:在异常块中检查并关闭未释放的文件句柄和SMTP连接,避免资源泄漏。

内容的提问来源于stack exchange,提问作者Rizwan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 13:15:24