oracle 发送邮件

来源:互联网 发布:ps4重新构建数据库 编辑:程序博客网 时间:2024/05/19 13:06
</pre><pre name="code" class="html">--确定是否安装了utl_mail,如果没有用下面的语句安装utl_mail@?/rdbms/admin/utlmail.sql@?/rdbms/admin/prvtmail.plb  --设置smtp_out_server参数--10g中oracle新增加了这个参数, 用来定义smtp服务器.如果该参数没有设置,oracle会自动解析db_domain 参数,用域名来实现mail的发送,没有db_domain也没有设置,那么mail将不会成功发送.建议设置smtp_out_server参数.    alter system set smtp_out_server='smtp.ym.163.com';--如果要同时设置多个smtp服务器,可以将各个服务器用逗号隔开.  alter system set smtp_out_server='mail.a.com:25,mail.b.com';--上面的域名也可以用ip来代替,oracle默认会使用25端口来发送,也可以手动指定具体的端口.  alter system set smtp_out_server='mail.a.com:25,mail.b.com:25';    --创建存放附件的目录.  mkdir -p /u01/mail/attachment  create directory export as '/u01/mail/attachment';  grant read,write on directory export to public;  SELECT utl_inaddr.get_host_address FROM dual; SELECT  utl_inaddr.get_host_name FROM dual;--给用户授权BEGIN  DBMS_NETWORK_ACL_ADMIN.CREATE_ACL(ACL         => 'mail_server.xml',                                    DESCRIPTION => 'Permission to make network connection to mail server',                                    PRINCIPAL   => 'RUDY',                                    IS_GRANT    => TRUE,                                    PRIVILEGE   => 'connect');  DBMS_NETWORK_ACL_ADMIN.ADD_PRIVILEGE(ACL       => 'mail_server.xml',                                       PRINCIPAL => 'RUDY',                                       IS_GRANT  => TRUE,                                       PRIVILEGE => 'resolve');  DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL(ACL => 'mail_server.xml', HOST => '*', LOWER_PORT => 25);END;--重启数据库--shutdown immediate;--startup;--删除上面的控制列表BEGINDBMS_NETWORK_ACL_ADMIN.drop_acl ( acl => 'mail_server.xml');END;--创建发送mail的存储存储过程CREATE OR REPLACE PROCEDURE SEND_MAIL(P_RECIPIENT VARCHAR2, -- 邮件接收人                                      P_SUBJECT   VARCHAR2, -- 邮件标题                                      P_MESSAGE   VARCHAR2 -- 邮件正文                                      ) IS  --下面四个变量请根据实际邮件服务器进行赋值  V_MAILHOST VARCHAR2(30) := 'smtp.ym.163.com'; --SMTP服务器地址   V_SENDOR              VARCHAR2(100) := 'mail_user_sendor';    V_SERVER              VARCHAR2(100) := 'mail_server';    V_USER                VARCHAR2(100) := 'mail_user';    V_PASS                VARCHAR2(100) := 'password';  V_CONN UTL_SMTP.CONNECTION; --到邮件服务器的连接  V_MSG  VARCHAR2(4000); --邮件内容BEGIN  V_CONN := UTL_SMTP.OPEN_CONNECTION(V_MAILHOST, 25);  UTL_SMTP.EHLO(V_CONN, V_MAILHOST); --是用 ehlo() 而不是 helo() 函数  --否则会报:ORA-29279: SMTP 永久性错误: 503 5.5.2 Send hello first.  UTL_SMTP.COMMAND(V_CONN, 'AUTH LOGIN'); -- smtp服务器登录校验  UTL_SMTP.COMMAND(V_CONN, UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(V_USER))));  UTL_SMTP.COMMAND(V_CONN, UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(V_PASS))));  UTL_SMTP.MAIL(V_CONN, V_SENDER); --设置发件人  UTL_SMTP.RCPT(V_CONN, P_RECIPIENT); --设置收件人  -- 创建要发送的邮件内容 注意报头信息和邮件正文之间要空一行  V_MSG := 'Date:' || TO_CHAR(SYSDATE, 'dd mon yy hh24:mi:ss') || UTL_TCP.CRLF || 'From: ' || V_SENDER || '<' ||           V_SENDER || '>' || UTL_TCP.CRLF || 'To: ' || P_RECIPIENT || '<' || P_RECIPIENT || '>' || UTL_TCP.CRLF ||           'Subject: ' || P_SUBJECT || UTL_TCP.CRLF || UTL_TCP.CRLF -- 这前面是报头信息           || P_MESSAGE; -- 这个是邮件正文  UTL_SMTP.OPEN_DATA(V_CONN); --打开流  UTL_SMTP.WRITE_RAW_DATA(V_CONN, UTL_RAW.CAST_TO_RAW(V_MSG)); --这样写标题和内容都能用中文  UTL_SMTP.CLOSE_DATA(V_CONN); --关闭流  UTL_SMTP.QUIT(V_CONN); --关闭连接EXCEPTION  WHEN OTHERS THEN    DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_STACK);    DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_CALL_STACK);END SEND_MAIL;   -- 没有附件的邮件发送示例:begin     send_mail( 'rudy.gao@laba.cn',' 测试?','这是一个测试'); end--发送中文邮件PROCEDURE PROC_SENDE_MAIL(P_TXT       VARCHAR2,                            P_SUB       VARCHAR2,                            P_RECEIVER  VARCHAR2,                            P_SENDOR    VARCHAR2 DEFAULT NULL,                            P_SERVER    VARCHAR2 DEFAULT NULL,                            P_PORT      NUMBER DEFAULT 25,                            P_NEED_SMTP INT DEFAULT 1,                            P_USER      VARCHAR2 DEFAULT NULL,                            P_PASS      VARCHAR2 DEFAULT NULL,                            P_FILENAME  VARCHAR2 DEFAULT NULL,                            P_ENCODE    VARCHAR2 DEFAULT 'base64') IS    L_CRLF          VARCHAR2(2) := UTL_TCP.CRLF;    L_SENDORADDRESS VARCHAR2(4000);    L_SPLITE        VARCHAR2(10) := '++';    BOUNDARY            CONSTANT VARCHAR2(256) := '-----BY ORACLE ADMIN';    FIRST_BOUNDARY      CONSTANT VARCHAR2(256) := '--' || BOUNDARY || L_CRLF;    LAST_BOUNDARY       CONSTANT VARCHAR2(256) := '--' || BOUNDARY || '--' || L_CRLF;    MULTIPART_MIME_TYPE CONSTANT VARCHAR2(256) := 'multipart/mixed; boundary="' || BOUNDARY || '"';    /* 以下部分是发送大二进制附件时用到的变量 */    L_FIL                 BFILE;    L_FILE_LEN            NUMBER;    L_MODULO              NUMBER;    L_PIECES              NUMBER;    L_FILE_HANDLE         UTL_FILE.FILE_TYPE;    L_AMT                 BINARY_INTEGER := 672 * 3; /* ensures proper format;  2016 */    L_FILEPOS             PLS_INTEGER := 1; /* pointer for the file */    L_CHUNKS              NUMBER;    L_BUF                 RAW(2100);    L_DATA                RAW(2100);    L_MAX_LINE_WIDTH      NUMBER := 54;    L_DIRECTORY_BASE_NAME VARCHAR2(100) := 'DIR_FOR_SEND_MAIL';    L_LINE                VARCHAR2(1000);    L_MESG                VARCHAR2(32767);    V_SENDOR              VARCHAR2(100) := 'mail_user_sendor';    V_SERVER              VARCHAR2(100) := 'mail_server';    V_USER                VARCHAR2(100) := 'mail_user';    V_PASS                VARCHAR2(100) := 'password';    /* 以上部分是发送大二进制附件时用到的变量 */    TYPE ADDRESS_LIST IS TABLE OF VARCHAR2(100) INDEX BY BINARY_INTEGER;    MY_ADDRESS_LIST ADDRESS_LIST;    TYPE ACCT_LIST IS TABLE OF VARCHAR2(100) INDEX BY BINARY_INTEGER;    MY_ACCT_LIST ACCT_LIST;    -------------------------------------返回附件源文件所在目录或者名称--------------------------------------    FUNCTION GET_FILE(P_FILE VARCHAR2, P_GET INT) RETURN VARCHAR2 IS      --p_get=1 表示返回目录      --p_get=2 表示返回文件名      L_FILE VARCHAR2(1000);    BEGIN      IF INSTR(P_FILE, '\') > 0 THEN        --windows        IF P_GET = 1 THEN          L_FILE := SUBSTR(P_FILE, 1, INSTR(P_FILE, '\', -1) - 1);        ELSIF P_GET = 2 THEN          L_FILE := SUBSTR(P_FILE, - (LENGTH(P_FILE) - INSTR(P_FILE, '\', -1)));        END IF;      ELSIF INSTR(P_FILE, '/') > 0 THEN        --linux/unix        IF P_GET = 1 THEN          L_FILE := SUBSTR(P_FILE, 1, INSTR(P_FILE, '/', -1) - 1);        ELSIF P_GET = 2 THEN          L_FILE := SUBSTR(P_FILE, - (LENGTH(P_FILE) - INSTR(P_FILE, '/', -1)));        END IF;      END IF;      RETURN L_FILE;    END;    ---------------------------------------------删除directory------------------------------------    PROCEDURE DROP_DIRECTORY(P_DIRECTORY_NAME VARCHAR2) IS    BEGIN      EXECUTE IMMEDIATE 'drop directory ' || P_DIRECTORY_NAME;    EXCEPTION      WHEN OTHERS THEN        NULL;    END;    --------------------------------------------------创建directory-----------------------------------------    PROCEDURE CREATE_DIRECTORY(P_DIRECTORY_NAME VARCHAR2, P_DIR VARCHAR2) IS    BEGIN      EXECUTE IMMEDIATE 'create directory ' || P_DIRECTORY_NAME || ' as ''' || P_DIR || '''';      EXECUTE IMMEDIATE 'grant read,write on directory ' || P_DIRECTORY_NAME || ' to public';    EXCEPTION      WHEN OTHERS THEN        RAISE;    END;    --------------------------------------------分割邮件地址或者附件地址-----------------------------------    PROCEDURE P_SPLITE_STR(P_STR VARCHAR2, P_SPLITE_FLAG INT DEFAULT 1) IS      L_ADDR VARCHAR2(254) := '';      L_LEN  INT;      L_STR  VARCHAR2(4000);      J      INT := 0; --表示邮件地址或者附件的个数    BEGIN      /*处理接收邮件地址列表,包括去空格、将;转换为,等*/      L_STR := TRIM(RTRIM(REPLACE(REPLACE(P_STR, ';', ','), ' ', ''), ','));      L_LEN := LENGTH(L_STR);      FOR I IN 1 .. L_LEN LOOP        IF SUBSTR(L_STR, I, 1) <> ',' THEN          L_ADDR := L_ADDR || SUBSTR(L_STR, I, 1);        ELSE          J := J + 1;          IF P_SPLITE_FLAG = 1 THEN            --表示处理邮件地址            --前后需要加上'<>',否则很多邮箱将不能发送邮件            L_ADDR := '<' || L_ADDR || '>';            --调用邮件发送过程            MY_ADDRESS_LIST(J) := L_ADDR;          ELSIF P_SPLITE_FLAG = 2 THEN            --表示处理附件名称            MY_ACCT_LIST(J) := L_ADDR;          END IF;          L_ADDR := '';        END IF;        IF I = L_LEN THEN          J := J + 1;          IF P_SPLITE_FLAG = 1 THEN            --调用邮件发送过程            L_ADDR := '<' || L_ADDR || '>';            MY_ADDRESS_LIST(J) := L_ADDR;          ELSIF P_SPLITE_FLAG = 2 THEN            MY_ACCT_LIST(J) := L_ADDR;          END IF;        END IF;      END LOOP;    END;    ------------------------------------------------写邮件头和邮件内容------------------------------------------    PROCEDURE WRITE_DATA(P_CONN   IN OUT NOCOPY UTL_SMTP.CONNECTION,                         P_NAME   IN VARCHAR2,                         P_VALUE  IN VARCHAR2,                         P_SPLITE VARCHAR2 DEFAULT ':',                         P_CRLF   VARCHAR2 DEFAULT L_CRLF) IS    BEGIN      /* utl_raw.cast_to_raw 对解决中文乱码问题很重要*/      UTL_SMTP.WRITE_RAW_DATA(P_CONN,                              UTL_RAW.CAST_TO_RAW(CONVERT(P_NAME || P_SPLITE || P_VALUE || P_CRLF, 'ZHS16GBK')));    END;    ----------------------------------------写MIME邮件尾部-----------------------------------------------------    PROCEDURE END_BOUNDARY(CONN IN OUT NOCOPY UTL_SMTP.CONNECTION, LAST IN BOOLEAN DEFAULT FALSE) IS    BEGIN      UTL_SMTP.WRITE_DATA(CONN, UTL_TCP.CRLF);      IF (LAST) THEN        UTL_SMTP.WRITE_DATA(CONN, LAST_BOUNDARY);      END IF;    END;    ----------------------------------------------发送附件----------------------------------------------------    PROCEDURE ATTACHMENT(CONN         IN OUT NOCOPY UTL_SMTP.CONNECTION,                         MIME_TYPE    IN VARCHAR2 DEFAULT 'text/plain',                         INLINE       IN BOOLEAN DEFAULT TRUE,                         FILENAME     IN VARCHAR2 DEFAULT 't.txt',                         TRANSFER_ENC IN VARCHAR2 DEFAULT '7 bit',                         DT_NAME      IN VARCHAR2 DEFAULT '0') IS          L_FILENAME VARCHAR2(1000);    BEGIN      --写附件头      UTL_SMTP.WRITE_DATA(CONN, FIRST_BOUNDARY);      --设置附件格式      WRITE_DATA(CONN, 'Content-Type', MIME_TYPE);      --如果文件名称非空,表示有附件      DROP_DIRECTORY(DT_NAME);      --创建directory      CREATE_DIRECTORY(DT_NAME, GET_FILE(FILENAME, 1));      --得到附件文件名称      L_FILENAME := GET_FILE(FILENAME, 2);      IF (INLINE) THEN        WRITE_DATA(CONN, 'Content-Disposition', 'inline; filename="' || L_FILENAME || '"');      ELSE        WRITE_DATA(CONN, 'Content-Disposition', 'attachment; filename="' || L_FILENAME || '"');      END IF;      --设置附件的转换格式      IF (TRANSFER_ENC IS NOT NULL) THEN        WRITE_DATA(CONN, 'Content-Transfer-Encoding', TRANSFER_ENC);      END IF;          UTL_SMTP.WRITE_DATA(CONN, UTL_TCP.CRLF);          --begin 贴附件内容      IF TRANSFER_ENC = 'bit 7' THEN        --如果是文本类型的附件        BEGIN          L_FILE_HANDLE := UTL_FILE.FOPEN(DT_NAME, L_FILENAME, 'r'); --打开文件          --把附件分成多份,这样可以发送超过32K的附件          LOOP            UTL_FILE.GET_LINE(L_FILE_HANDLE, L_LINE);            L_MESG := L_LINE || L_CRLF;            WRITE_DATA(CONN, '', L_MESG, '', '');          END LOOP;          UTL_FILE.FCLOSE(L_FILE_HANDLE);          END_BOUNDARY(CONN);        EXCEPTION          WHEN OTHERS THEN            UTL_FILE.FCLOSE(L_FILE_HANDLE);            END_BOUNDARY(CONN);            NULL;        END; --结束文本类型附件的处理            ELSIF TRANSFER_ENC = 'base64' THEN        --如果是二进制类型的附件        BEGIN          --把附件分成多份,这样可以发送超过32K的附件          L_FILEPOS  := 1; --重置offset,在发送多个附件时,必须重置          L_FIL      := BFILENAME(DT_NAME, L_FILENAME);          L_FILE_LEN := DBMS_LOB.GETLENGTH(L_FIL);          L_MODULO   := MOD(L_FILE_LEN, L_AMT);          L_PIECES   := TRUNC(L_FILE_LEN / L_AMT);          IF (L_MODULO <> 0) THEN            L_PIECES := L_PIECES + 1;          END IF;          DBMS_LOB.FILEOPEN(L_FIL, DBMS_LOB.FILE_READONLY);          DBMS_LOB.READ(L_FIL, L_AMT, L_FILEPOS, L_BUF);          L_DATA := NULL;          FOR I IN 1 .. L_PIECES LOOP            L_FILEPOS  := I * L_AMT + 1;            L_FILE_LEN := L_FILE_LEN - L_AMT;            L_DATA     := UTL_RAW.CONCAT(L_DATA, L_BUF);            L_CHUNKS   := TRUNC(UTL_RAW.LENGTH(L_DATA) / L_MAX_LINE_WIDTH);            IF (I <> L_PIECES) THEN              L_CHUNKS := L_CHUNKS - 1;            END IF;            UTL_SMTP.WRITE_RAW_DATA(CONN, UTL_ENCODE.BASE64_ENCODE(L_DATA));            L_DATA := NULL;            IF (L_FILE_LEN < L_AMT AND L_FILE_LEN > 0) THEN              L_AMT := L_FILE_LEN;            END IF;            DBMS_LOB.READ(L_FIL, L_AMT, L_FILEPOS, L_BUF);          END LOOP;          DBMS_LOB.FILECLOSE(L_FIL);          END_BOUNDARY(CONN);        EXCEPTION          WHEN OTHERS THEN            DBMS_LOB.FILECLOSE(L_FIL);            END_BOUNDARY(CONN);            RAISE;        END; --结束处理二进制附件            END IF; --结束处理附件内容      DROP_DIRECTORY(DT_NAME);    END; --结束过程ATTACHMENT    ---------------------------------------------真正发送邮件的过程--------------------------------------------    PROCEDURE P_EMAIL(V_SENDORADDRESS2   VARCHAR2, --发送地址                      P_RECEIVERADDRESS2 VARCHAR2) --接受地址     IS      L_CONN UTL_SMTP.CONNECTION; --定义连接    BEGIN      /*初始化邮件服务器信息,连接邮件服务器*/      L_CONN := UTL_SMTP.OPEN_CONNECTION(V_SERVER, P_PORT);      UTL_SMTP.EHLO(L_CONN, V_SERVER);      --UTL_SMTP.HELO(L_CONN, V_SERVER);      /* smtp服务器登录校验 */      IF P_NEED_SMTP = 1 THEN        UTL_SMTP.COMMAND(L_CONN, 'AUTH LOGIN');        UTL_SMTP.COMMAND(L_CONN, UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(V_USER))));        UTL_SMTP.COMMAND(L_CONN, UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(V_PASS))));      END IF;          /*设置发送地址和接收地址*/      UTL_SMTP.MAIL(L_CONN, V_SENDORADDRESS2);      UTL_SMTP.RCPT(L_CONN, P_RECEIVERADDRESS2);          /*设置邮件头*/      UTL_SMTP.OPEN_DATA(L_CONN);          WRITE_DATA(L_CONN, 'Date', TO_CHAR(SYSDATE, 'yyyy-mm-dd hh24:mi:ss'));      /*设置发送人*/      WRITE_DATA(L_CONN, 'From', V_SENDOR);      /*设置接收人*/      WRITE_DATA(L_CONN, 'To', P_RECEIVER);      /*设置邮件主题*/      WRITE_DATA(L_CONN, 'Subject', P_SUB);          WRITE_DATA(L_CONN, 'Content-Type', MULTIPART_MIME_TYPE);      UTL_SMTP.WRITE_DATA(L_CONN, UTL_TCP.CRLF);      UTL_SMTP.WRITE_DATA(L_CONN, FIRST_BOUNDARY);      WRITE_DATA(L_CONN, 'Content-Type', 'text/plain;charset=gb2312');      --单独空一行,否则,正文内容不显示      UTL_SMTP.WRITE_DATA(L_CONN, UTL_TCP.CRLF);      /* 设置邮件正文        把分隔符还原成chr(10)。这主要是为了shell中调用该过程,如果有多行,则先把多行的内容合并成一行,并用 l_splite分隔        然后用 l_crlf替换chr(10)。这一步是必须的,否则将不能发送邮件正文有多行的邮件      */      WRITE_DATA(L_CONN, '', REPLACE(REPLACE(P_TXT, L_SPLITE, CHR(10)), CHR(10), L_CRLF), '', '');      END_BOUNDARY(L_CONN);          --如果文件名称不为空,则发送附件      IF (P_FILENAME IS NOT NULL) THEN        --根据逗号或者分号拆分附件地址        P_SPLITE_STR(P_FILENAME, 2);        --循环发送附件(在同一个邮件中)        FOR K IN 1 .. MY_ACCT_LIST.COUNT LOOP          ATTACHMENT(CONN         => L_CONN,                     FILENAME     => MY_ACCT_LIST(K),                     TRANSFER_ENC => P_ENCODE,                     DT_NAME      => L_DIRECTORY_BASE_NAME || TO_CHAR(K));        END LOOP;      END IF;          /*关闭数据写入*/      UTL_SMTP.CLOSE_DATA(L_CONN);      /*关闭连接*/      UTL_SMTP.QUIT(L_CONN);          /*异常处理*/    EXCEPTION      WHEN OTHERS THEN        NULL;        RAISE;          END;    ---------------------------------------------------主过程-----------------------------------------------------  BEGIN      IF P_SENDOR IS NOT NULL THEN      V_SENDOR := P_SENDOR;    END IF;    IF P_SERVER IS NOT NULL THEN      V_SERVER := P_SERVER;    END IF;    IF P_USER IS NOT NULL THEN      V_USER := P_USER;    END IF;    IF P_PASS IS NOT NULL THEN      V_PASS := P_PASS;    END IF;      L_SENDORADDRESS := '<' || V_SENDOR || '>';    P_SPLITE_STR(P_RECEIVER); --处理邮件地址    FOR K IN 1 .. MY_ADDRESS_LIST.COUNT LOOP      P_EMAIL(L_SENDORADDRESS, MY_ADDRESS_LIST(K));    END LOOP;    /*处理邮件地址,根据逗号分割邮件*/  EXCEPTION    WHEN OTHERS THEN      RAISE;  END PROC_SENDE_MAIL;/


0 0
原创粉丝点击