服务器之家

服务器之家 > 正文

oracle 发送邮件 实现方法

时间:2019-10-31 16:17     来源/作者:oracle教程网
  1. CREATE OR REPLACE PROCEDURE PROCSENDEMAIL(P_TXT        VARCHAR2, 
  2.                                            P_SUB        VARCHAR2, 
  3.                                            P_SENDOR     VARCHAR2, 
  4.                                            P_RECEIVER   VARCHAR2, 
  5.                                            P_SERVER     VARCHAR2, 
  6.                                            P_PORT       NUMBER DEFAULT 25, 
  7.                                            P_NEED_SMTP INT DEFAULT 0, 
  8.                                            P_USER       VARCHAR2 DEFAULT NULL, 
  9.                                            P_PASS       VARCHAR2 DEFAULT NULL, 
  10.                                            P_FILENAME   VARCHAR2 DEFAULT NULL, 
  11.                                            P_ENCODE     VARCHAR2 DEFAULT 'bit 7'
  12.    AUTHID CURRENT_USER IS 
  13.    /* 
  14.    作用:用oracle发送邮件 
  15.    主要功能:1、支持多收件人。 
  16.              2、支持中文 
  17.              3、支持抄送人 
  18.              4、支持大于32K的附件 
  19.              5、支持多行正文 
  20.              6、支持多附件 
  21.              7、支持文本附件和二进制附件 
  22.              8、支持HTML格式 
  23.              8、支持 
  24.    作者:suk 
  25.    参数说明: 
  26.              p_txt :邮件正文 
  27.              p_sub: 邮件标题 
  28.              p_SendorAddress : 发送人邮件地址 
  29.              p_ReceiverAddress : 接收地址,可以同时发送到多个地址上,地址之间用","或者";"隔开 
  30.              p_EmailServer : 邮件服务器地址,可以是域名或者IP 
  31.              p_Port :邮件服务器端口 
  32.              p_need_smtp:是否需要smtp认证,0表示不需要,1表示需要 
  33.              p_user:smtp验证需要的用户名 
  34.              p_pass:smtp验证需要的密码 
  35.              p_filename:附件名称,必须包含完整的路径,如"d:tempa.txt"。 
  36.                          可以有多个附件,附件名称只见用逗号或者分号分隔 
  37.              p_encode:附件编码转换格式,其中 p_encode='bit 7' 表示文本类型附件 
  38.                                               p_encode='base64' 表示二进制类型附件 
  39.    注意: 
  40.          1、对于文本类型的附件,不能用base64的方式发送,否则出错 
  41.          2、对于多个附件只能用同一种格式发送 
  42.    */ 
  43.    L_CRLF VARCHAR2(2) := UTL_TCP.CRLF; 
  44.    L_SENDORADDRESS VARCHAR2(4000); 
  45.    L_SPLITE         VARCHAR2(10) := '++'
  46.    BOUNDARY             CONSTANT VARCHAR2(256) := '-----BYSUK'
  47.    FIRST_BOUNDARY       CONSTANT VARCHAR2(256) := '--' || BOUNDARY || L_CRLF; 
  48.    LAST_BOUNDARY        CONSTANT VARCHAR2(256) := '--' || BOUNDARY || '--' || 
  49.                                                  L_CRLF; 
  50.    MULTIPART_MIME_TYPE CONSTANT VARCHAR2(256) := 'multipart/mixed; boundary="' || 
  51.                                                  BOUNDARY || '"'
  52.    /* 以下部分是发送大二进制附件时用到的变量 */ 
  53.    L_FIL                  BFILE; 
  54.    L_FILE_LEN             NUMBER; 
  55.    L_MODULO               NUMBER; 
  56.    L_PIECES               NUMBER; 
  57.    L_FILE_HANDLE          UTL_FILE.FILE_TYPE; 
  58.    L_AMT                  BINARY_INTEGER := 672 * 3; /* ensures proper format;   2016 */ 
  59.    L_FILEPOS              PLS_INTEGER := 1; /* pointer for the file */ 
  60.    L_CHUNKS               NUMBER; 
  61.    L_BUF                  RAW(2100); 
  62.    L_DATA                 RAW(2100); 
  63.    L_MAX_LINE_WIDTH       NUMBER := 54; 
  64.    L_DIRECTORY_BASE_NAME VARCHAR2(100) := 'DIR_FOR_SEND_MAIL'
  65.    L_LINE                 VARCHAR2(1000); 
  66.    L_MESG                 VARCHAR2(32767); 
  67.    /* 以上部分是发送大二进制附件时用到的变量 */ 
  68.    TYPE ADDRESS_LIST IS TABLE OF VARCHAR2(100) INDEX BY BINARY_INTEGER; 
  69.    MY_ADDRESS_LIST ADDRESS_LIST; 
  70.    TYPE ACCT_LIST IS TABLE OF VARCHAR2(100) INDEX BY BINARY_INTEGER; 
  71.    MY_ACCT_LIST ACCT_LIST; 
  72.    -------------------------------------返回附件源文件所在目录或者名称-------------------------------------- 
  73.    FUNCTION GET_FILE(P_FILE VARCHAR2, 
  74.                      P_GET   INT) RETURN VARCHAR2 IS 
  75.      --p_get=1 表示返回目录 
  76.      --p_get=2 表示返回文件名 
  77.      L_FILE VARCHAR2(1000); 
  78.    BEGIN 
  79.      IF INSTR(P_FILE, '') > 0 THEN 
  80.        --windows 
  81.        IF P_GET = 1 THEN 
  82.          L_FILE := SUBSTR(P_FILE, 1, INSTR(P_FILE, '', -1) - 1); 
  83.        ELSIF P_GET = 2 THEN 
  84.          L_FILE := SUBSTR(P_FILE, - (LENGTH(P_FILE) - INSTR(P_FILE, '', -1))); 
  85.        END IF; 
  86.      ELSIF INSTR(P_FILE, '/') > 0 THEN 
  87.        --linux/unix 
  88.        IF P_GET = 1 THEN 
  89.          L_FILE := SUBSTR(P_FILE, 1, INSTR(P_FILE, '/', -1) - 1); 
  90.        ELSIF P_GET = 2 THEN 
  91.          L_FILE := SUBSTR(P_FILE, - (LENGTH(P_FILE) - INSTR(P_FILE, '/', -1))); 
  92.        END IF; 
  93.      END IF; 
  94.      RETURN L_FILE; 
  95.    END; 
  96.    ---------------------------------------------删除directory------------------------------------ 
  97.    PROCEDURE DROP_DIRECTORY(P_DIRECTORY_NAME VARCHAR2) IS 
  98.    BEGIN 
  99.      EXECUTE IMMEDIATE 'drop directory ' || P_DIRECTORY_NAME; 
  100.    EXCEPTION 
  101.      WHEN OTHERS THEN 
  102.        NULL; 
  103.    END; 
  104.    --------------------------------------------------创建directory----------------------------------------- 
  105.    PROCEDURE CREATE_DIRECTORY(P_DIRECTORY_NAME VARCHAR2, 
  106.                               P_DIR             VARCHAR2) IS 
  107.    BEGIN 
  108.      EXECUTE IMMEDIATE 'create directory ' || P_DIRECTORY_NAME || ' as ''' || 
  109.                        P_DIR || ''''
  110.      EXECUTE IMMEDIATE 'grant read,write on directory ' || P_DIRECTORY_NAME || 
  111.                        ' to public'
  112.      EXCEPTION 
  113.      WHEN OTHERS THEN 
  114.        RAISE; 
  115.    END; 
  116.    --------------------------------------------分割邮件地址或者附件地址----------------------------------- 
  117.    PROCEDURE P_SPLITE_STR(P_STR          VARCHAR2, 
  118.                           P_SPLITE_FLAG INT DEFAULT 1) IS 
  119.      L_ADDR VARCHAR2(254) := ''
  120.      L_LEN   INT; 
  121.      L_STR   VARCHAR2(4000); 
  122.      J       INT := 0; --表示邮件地址或者附件的个数 
  123.    BEGIN 
  124.      /*处理接收邮件地址列表,包括去空格、将;转换为,等*/ 
  125.      L_STR := TRIM(RTRIM(REPLACE(REPLACE(P_STR, ';'','), ' '''), ',')); 
  126.      L_LEN := LENGTH(L_STR); 
  127.      FOR I IN 1 .. L_LEN LOOP 
  128.        IF SUBSTR(L_STR, I, 1) <> ',' THEN 
  129.          L_ADDR := L_ADDR || SUBSTR(L_STR, I, 1); 
  130.        ELSE 
  131.          J := J + 1; 
  132.          IF P_SPLITE_FLAG = 1 THEN --表示处理邮件地址        
  133.            --前后需要加上'<>',否则很多邮箱将不能发送邮件 
  134.            L_ADDR := '<' || L_ADDR || '>'
  135.            --调用邮件发送过程 
  136.            MY_ADDRESS_LIST(J) := L_ADDR; 
  137.          ELSIF P_SPLITE_FLAG = 2 THEN --表示处理附件名称 
  138.            MY_ACCT_LIST(J) := L_ADDR; 
  139.          END IF; 
  140.          L_ADDR := ''
  141.        END IF; 
  142.        IF I = L_LEN THEN 
  143.          J := J + 1; 
  144.          IF P_SPLITE_FLAG = 1 THEN 
  145.            --调用邮件发送过程 
  146.            L_ADDR := '<' || L_ADDR || '>'
  147.            MY_ADDRESS_LIST(J) := L_ADDR; 
  148.          ELSIF P_SPLITE_FLAG = 2 THEN 
  149.            MY_ACCT_LIST(J) := L_ADDR; 
  150.          END IF; 
  151.        END IF; 
  152.      END LOOP; 
  153.    END; 
  154.    ------------------------------------------------写邮件头和邮件内容------------------------------------------ 
  155.    PROCEDURE WRITE_DATA(P_CONN    IN OUT NOCOPY UTL_SMTP.CONNECTION, 
  156.                         P_NAME    IN VARCHAR2, 
  157.                         P_VALUE   IN VARCHAR2, 
  158.                         P_SPLITE VARCHAR2 DEFAULT ':'
  159.                         P_CRLF    VARCHAR2 DEFAULT L_CRLF) IS 
  160.    BEGIN 
  161.      /* utl_raw.cast_to_raw 对解决中文乱码问题很重要*/ 
  162.      UTL_SMTP.WRITE_RAW_DATA(P_CONN, UTL_RAW.CAST_TO_RAW(CONVERT(P_NAME || 
  163.                                                           P_SPLITE || 
  164.                                                           P_VALUE || 
  165.                                                           P_CRLF, 'ZHS16GBK'))); 
  166.    END; 
  167.    ----------------------------------------写MIME邮件尾部----------------------------------------------------- 
  168.    PROCEDURE END_BOUNDARY(CONN IN OUT NOCOPY UTL_SMTP.CONNECTION, 
  169.                           LAST IN BOOLEAN DEFAULT FALSE) IS 
  170.    BEGIN 
  171.      UTL_SMTP.WRITE_DATA(CONN, UTL_TCP.CRLF); 
  172.      IF (LAST) THEN 
  173.        UTL_SMTP.WRITE_DATA(CONN, LAST_BOUNDARY); 
  174.      END IF; 
  175.    END; 
  176.    ----------------------------------------------发送附件---------------------------------------------------- 
  177.    PROCEDURE ATTACHMENT(CONN          IN OUT NOCOPY UTL_SMTP.CONNECTION, 
  178.                         MIME_TYPE     IN VARCHAR2 DEFAULT 'text/plain'
  179.                         INLINE        IN BOOLEAN DEFAULT TRUE, 
  180.                         FILENAME      IN VARCHAR2 DEFAULT 't.txt'
  181.                         TRANSFER_ENC IN VARCHAR2 DEFAULT '7 bit'
  182.                         DT_NAME       IN VARCHAR2 DEFAULT '0') IS 
  183.  
  184.      L_FILENAME VARCHAR2(1000); 
  185.    BEGIN 
  186.      --写附件头 
  187.      UTL_SMTP.WRITE_DATA(CONN, FIRST_BOUNDARY); 
  188.      --设置附件格式 
  189.      WRITE_DATA(CONN, 'Content-Type', MIME_TYPE); 
  190.      --如果文件名称非空,表示有附件 
  191.      DROP_DIRECTORY(DT_NAME); 
  192.      --创建directory 
  193.      CREATE_DIRECTORY(DT_NAME, GET_FILE(FILENAME, 1)); 
  194.      --得到附件文件名称 
  195.      L_FILENAME := GET_FILE(FILENAME, 2); 
  196.      IF (INLINE) THEN 
  197.        WRITE_DATA(CONN, 'Content-Disposition''inline; filename="' || 
  198.                    L_FILENAME || '"'); 
  199.      ELSE 
  200.        WRITE_DATA(CONN, 'Content-Disposition''attachment; filename="' || 
  201.                    L_FILENAME || '"'); 
  202.      END IF; 
  203.      --设置附件的转换格式 
  204.      IF (TRANSFER_ENC IS NOT NULL) THEN 
  205.        WRITE_DATA(CONN, 'Content-Transfer-Encoding', TRANSFER_ENC); 
  206.      END IF; 
  207.  
  208.      UTL_SMTP.WRITE_DATA(CONN, UTL_TCP.CRLF); 
  209.  
  210.      --begin 贴附件内容 
  211.      IF TRANSFER_ENC = 'bit 7' THEN 
  212.        --如果是文本类型的附件 
  213.        BEGIN 
  214.          L_FILE_HANDLE := UTL_FILE.FOPEN(DT_NAME, L_FILENAME, 'r'); --打开文件 
  215.          --把附件分成多份,这样可以发送超过32K的附件 
  216.          LOOP 
  217.            UTL_FILE.GET_LINE(L_FILE_HANDLE, L_LINE); 
  218.            L_MESG := L_LINE || L_CRLF; 
  219.            WRITE_DATA(CONN, '', L_MESG, ''''); 
  220.          END LOOP; 
  221.          UTL_FILE.FCLOSE(L_FILE_HANDLE); 
  222.          END_BOUNDARY(CONN); 
  223.        EXCEPTION 
  224.          WHEN OTHERS THEN 
  225.            UTL_FILE.FCLOSE(L_FILE_HANDLE); 
  226.            END_BOUNDARY(CONN); 
  227.            NULL; 
  228.        END; --结束文本类型附件的处理 
  229.  
  230.      ELSIF TRANSFER_ENC = 'base64' THEN 
  231.        --如果是二进制类型的附件 
  232.        BEGIN 
  233.          --把附件分成多份,这样可以发送超过32K的附件 
  234.          L_FILEPOS   := 1;--重置offset,在发送多个附件时,必须重置 
  235.          L_FIL       := BFILENAME(DT_NAME, L_FILENAME); 
  236.          L_FILE_LEN := DBMS_LOB.GETLENGTH(L_FIL); 
  237.          L_MODULO    := MOD(L_FILE_LEN, L_AMT); 
  238.          L_PIECES    := TRUNC(L_FILE_LEN / L_AMT); 
  239.          IF (L_MODULO <> 0) THEN 
  240.            L_PIECES := L_PIECES + 1; 
  241.          END IF; 
  242.          DBMS_LOB.FILEOPEN(L_FIL, DBMS_LOB.FILE_READONLY); 
  243.          DBMS_LOB.READ(L_FIL, L_AMT, L_FILEPOS, L_BUF); 
  244.          L_DATA := NULL; 
  245.          FOR I IN 1 .. L_PIECES LOOP 
  246.            L_FILEPOS   := I * L_AMT + 1; 
  247.            L_FILE_LEN := L_FILE_LEN - L_AMT; 
  248.            L_DATA      := UTL_RAW.CONCAT(L_DATA, L_BUF); 
  249.            L_CHUNKS    := TRUNC(UTL_RAW.LENGTH(L_DATA) / L_MAX_LINE_WIDTH); 
  250.            IF (I <> L_PIECES) THEN 
  251.              L_CHUNKS := L_CHUNKS - 1; 
  252.            END IF; 
  253.            UTL_SMTP.WRITE_RAW_DATA(CONN, UTL_ENCODE.BASE64_ENCODE(L_DATA)); 
  254.            L_DATA := NULL; 
  255.            IF (L_FILE_LEN < L_AMT AND L_FILE_LEN > 0) THEN 
  256.              L_AMT := L_FILE_LEN; 
  257.            END IF; 
  258.            DBMS_LOB.READ(L_FIL, L_AMT, L_FILEPOS, L_BUF); 
  259.          END LOOP; 
  260.          DBMS_LOB.FILECLOSE(L_FIL); 
  261.          END_BOUNDARY(CONN); 
  262.        EXCEPTION 
  263.          WHEN OTHERS THEN 
  264.            DBMS_LOB.FILECLOSE(L_FIL); 
  265.            END_BOUNDARY(CONN); 
  266.            RAISE; 
  267.        END; --结束处理二进制附件 
  268.  
  269.      END IF; --结束处理附件内容 
  270.      DROP_DIRECTORY(DT_NAME); 
  271.    END; --结束过程ATTACHMENT 
  272.    ---------------------------------------------真正发送邮件的过程-------------------------------------------- 
  273.    PROCEDURE P_EMAIL(P_SENDORADDRESS2    VARCHAR2, --发送地址 
  274.                      P_RECEIVERADDRESS2 VARCHAR2) --接受地址 
  275.     IS 
  276.      L_CONN UTL_SMTP.CONNECTION; --定义连接 
  277.    BEGIN 
  278.      /*初始化邮件服务器信息,连接邮件服务器*/ 
  279.      L_CONN := UTL_SMTP.OPEN_CONNECTION(P_SERVER, P_PORT); 
  280.      UTL_SMTP.HELO(L_CONN, P_SERVER); 
  281.      /* smtp服务器登录校验 */ 
  282.      IF P_NEED_SMTP = 1 THEN 
  283.        UTL_SMTP.COMMAND(L_CONN, 'AUTH LOGIN'''); 
  284.        UTL_SMTP.COMMAND(L_CONN, UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(P_USER)))); 
  285.        UTL_SMTP.COMMAND(L_CONN, UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(P_PASS)))); 
  286.      END IF; 
  287.  
  288.      /*设置发送地址和接收地址*/ 
  289.      UTL_SMTP.MAIL(L_CONN, P_SENDORADDRESS2); 
  290.      UTL_SMTP.RCPT(L_CONN, P_RECEIVERADDRESS2); 
  291.  
  292.      /*设置邮件头*/ 
  293.      UTL_SMTP.OPEN_DATA(L_CONN); 
  294.  
  295.      WRITE_DATA(L_CONN, 'Date', TO_CHAR(SYSDATE, 'yyyy-mm-dd hh24:mi:ss')); 
  296.      /*设置发送人*/ 
  297.      WRITE_DATA(L_CONN, 'From', P_SENDOR); 
  298.      /*设置接收人*/ 
  299.      WRITE_DATA(L_CONN, 'To', P_RECEIVER); 
  300.      /*设置邮件主题*/ 
  301.      WRITE_DATA(L_CONN, 'Subject', P_SUB); 
  302.  
  303.      WRITE_DATA(L_CONN, 'Content-Type', MULTIPART_MIME_TYPE); 
  304.      UTL_SMTP.WRITE_DATA(L_CONN, UTL_TCP.CRLF); 
  305.      UTL_SMTP.WRITE_DATA(L_CONN, FIRST_BOUNDARY); 
  306.      WRITE_DATA(L_CONN, 'Content-Type''text/plain;charset=gb2312'); 
  307.      --单独空一行,否则,正文内容不显示 
  308.      UTL_SMTP.WRITE_DATA(L_CONN, UTL_TCP.CRLF); 
  309.      /* 设置邮件正文 
  310.        把分隔符还原成chr(10)。这主要是为了shell中调用该过程,如果有多行,则先把多行的内容合并成一行, 
  311. 并用 l_splite分隔  然后用 l_crlf替换chr(10)。这一步是必须的,否则将不能发送邮件正文有多行的邮件 
  312.  
  313.      */ 
  314.      WRITE_DATA(L_CONN, '', REPLACE(REPLACE(P_TXT, L_SPLITE, CHR(10)), CHR(10), L_CRLF), ''''); 
  315.      END_BOUNDARY(L_CONN); 
  316.  
  317.    --如果文件名称不为空,则发送附件 
  318.      IF (P_FILENAME IS NOT NULL) THEN 
  319.        --根据逗号或者分号拆分附件地址 
  320.        P_SPLITE_STR(P_FILENAME, 2); 
  321.        --循环发送附件(在同一个邮件中) 
  322.        FOR K IN 1 .. MY_ACCT_LIST.COUNT LOOP 
  323.          ATTACHMENT(CONN => L_CONN, FILENAME => MY_ACCT_LIST(K), TRANSFER_ENC =>  
  324. P_ENCODE, DT_NAME => L_DIRECTORY_BASE_NAME || 
  325.                                 TO_CHAR(K)); 
  326.        END LOOP; 
  327.      END IF; 
  328.  
  329.      /*关闭数据写入*/ 
  330.      UTL_SMTP.CLOSE_DATA(L_CONN); 
  331.      /*关闭连接*/ 
  332.      UTL_SMTP.QUIT(L_CONN); 
  333.  
  334.      /*异常处理*/ 
  335.    EXCEPTION 
  336.      WHEN OTHERS THEN 
  337.        NULL; 
  338.        RAISE; 
  339.  
  340.    END; 
  341.    ---------------------------------------------------主过程----------------------------------------------------- 
  342. BEGIN 
  343.    L_SENDORADDRESS := '<' || P_SENDOR || '>'
  344.    P_SPLITE_STR(P_RECEIVER);--处理邮件地址 
  345.    FOR K IN 1 .. MY_ADDRESS_LIST.COUNT LOOP 
  346.      P_EMAIL(L_SENDORADDRESS, MY_ADDRESS_LIST(K)); 
  347.    END LOOP; 
  348.    /*处理邮件地址,根据逗号分割邮件*/ 
  349. EXCEPTION 
  350.    WHEN OTHERS THEN 
  351.      RAISE; 
  352. END; 

 

使用实例:

  1. SQL>set serverout on 
  2.  
  3.  
  4. SQL> exec PROCSENDEMAIL('中文测试邮件','中文主题','space6212@163.com','space6212@163.com 
  5.  
  6. ,susk@souchang.com','202.108.5.85',25,1,'xxxx','xxxx','/tmp/a.jpg,/tmp/b.jpg','base64'); 
  7.  
  8.  
  9. PL/SQL procedure successfully completed. 
标签:

相关文章

热门资讯

玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分
玄元剑仙肉身有什么用 玄元剑仙肉身境界等级划分 2019-06-21
男生常说24816是什么意思?女生说13579是什么意思?
男生常说24816是什么意思?女生说13579是什么意思? 2019-09-17
华为nova5pro和p30pro哪个好 华为nova5pro和华为p30pro对比详情
华为nova5pro和p30pro哪个好 华为nova5pro和华为p30pro对比详情 2019-06-22
超A是什么意思 你好a表达的是什么
超A是什么意思 你好a表达的是什么 2019-06-06
抖音撒撒累累是什么歌 撒撒累累张艺兴歌曲名字
抖音撒撒累累是什么歌 撒撒累累张艺兴歌曲名字 2019-06-05
返回顶部