oracle 傳送郵件 實現方法

126雲發表於2021-01-09

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. 

遊戲伺服器

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69957453/viewspace-2748652/,如需轉載,請註明出處,否則將追究法律責任。

相關文章