去年因為 Alert 的問題, 上 Google 找到了 昭佑.天翔 的 Blog, 這位高手有客製了一個名為 "Graphic Alert", 當下真是驚為天人, 在天翔好心的指點之下, 我知道 UTL_SMTP 的存在

最近因為有些 Alert 因為資料量太大的問題 (超過 Alert 限制的 64K) (註一), 造成 Alert 無法正常寄送, 所以才認真的研究 UTL_SMTP 的實作. 剛好, 在 Oracle Metalink 上有一篇文章, 就是教我們怎麼使用 UTL_SMTP (Metalink DocID : 357385.1), 也附上完整範例檔, 基本上, 如果 DB Server 有開啟 SMTP 服務, 修正一下 Package 的設定, 應該就可以直接使用了. 不過, 可能會遇到的問題有 :


1. 中文字變亂碼
2. 內容太長, VARCHAR2 變數仍有長度問題
3. Attachment 的方式太過僵化

註一: Alert 64K 的限制在 Oracle Applications Technology 11i.ATG_PF.H.delta.7 (RUP 7) 將會更新, 不過, 上 RUP7 是一個不小的挑戰就是了

附上範例檔 :

/**********************Start of package spec for the E-mail program*******************/

/* demomail.sql */

CREATE OR REPLACE PACKAGE DEMO_MAIL IS
   /* Package variable Declaration. */

   SMTP_HOST                        VARCHAR2 (256) := 'Your SMTP Server Name or IP Address';
   SMTP_PORT                        PLS_INTEGER := 25;
   SMTP_DOMAIN                      VARCHAR2 (256) := 'Put your domain here';
   MAILER_ID CONSTANT               VARCHAR2 (256) := 'Mailer by Oracle UTL_SMTP';
   /* Boundary is an arbitrary string used as seperator marker for different
      sections of the of the e-mail. Separates various parts of e-mail and attachments. */


   BOUNDARY CONSTANT                VARCHAR2 (256) := '__7D81B75CCC90D2974F7A1CBD__';
   FIRST_BOUNDARY CONSTANT          VARCHAR2 (256) := '--' || BOUNDARY || UTL_TCP.CRLF;
   LAST_BOUNDARY CONSTANT           VARCHAR2 (256) := '--' || BOUNDARY || '--' || UTL_TCP.CRLF;
   MULTIPART_MIME_TYPE CONSTANT     VARCHAR2 (256) := 'multipart/mixed; boundary="' || BOUNDARY || '"';
   MAX_BASE64_LINE_WIDTH CONSTANT   PLS_INTEGER := 76 / 4 * 3;                               -- do not change this line.

   /* Table declaration for storing e-mail attachment file names. */

   TYPE ATTACHMENT IS RECORD (FILENAME VARCHAR2 (100));

   TYPE TAB_OF_ATTACHMENTS IS TABLE OF ATTACHMENT;

   /* Package Function and procedures. */

   PROCEDURE MAIL (SENDER       IN VARCHAR2
                 , RECIPIENTS   IN VARCHAR2
                 , CC           IN VARCHAR2 DEFAULT NULL
                 , BCC          IN VARCHAR2 DEFAULT NULL
                 , SUBJECT      IN VARCHAR2
                 , MESSAGE      IN VARCHAR2
                  );

   FUNCTION BEGIN_MAIL (SENDER       IN VARCHAR2
                      , RECIPIENTS   IN VARCHAR2
                      , CC           IN VARCHAR2 DEFAULT NULL
                      , BCC          IN VARCHAR2 DEFAULT NULL
                      , SUBJECT      IN VARCHAR2
                      , MIME_TYPE    IN VARCHAR2 DEFAULT 'text/plain'
                      , PRIORITY     IN PLS_INTEGER DEFAULT NULL
                       )
      RETURN UTL_SMTP.CONNECTION;

   PROCEDURE WRITE_TEXT (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION, MESSAGE IN VARCHAR2);

   PROCEDURE WRITE_MB_TEXT (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION, MESSAGE IN VARCHAR2);

   PROCEDURE WRITE_RAW (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION, MESSAGE IN RAW);

   PROCEDURE ATTACH_TEXT (CONN        IN OUT NOCOPY UTL_SMTP.CONNECTION
                        , DATA        IN            VARCHAR2
                        , MIME_TYPE   IN            VARCHAR2 DEFAULT 'text/plain'
                        , INLINE      IN            BOOLEAN DEFAULT TRUE
                        , FILENAME    IN            VARCHAR2 DEFAULT NULL
                        , LAST        IN            BOOLEAN DEFAULT FALSE
                         );

   PROCEDURE ATTACH_MB_TEXT (CONN        IN OUT NOCOPY UTL_SMTP.CONNECTION
                           , DATA        IN            VARCHAR2
                           , MIME_TYPE   IN            VARCHAR2 DEFAULT 'text/plain'
                           , INLINE      IN            BOOLEAN DEFAULT TRUE
                           , FILENAME    IN            VARCHAR2 DEFAULT NULL
                           , LAST        IN            BOOLEAN DEFAULT FALSE
                            );

   PROCEDURE ATTACH_BASE64 (CONN        IN OUT NOCOPY UTL_SMTP.CONNECTION
                          , DATA        IN            RAW
                          , MIME_TYPE   IN            VARCHAR2 DEFAULT 'application/octet'
                          , INLINE      IN            BOOLEAN DEFAULT TRUE
                          , FILENAME    IN            VARCHAR2 DEFAULT NULL
                          , LAST        IN            BOOLEAN DEFAULT FALSE
                           );

   PROCEDURE BEGIN_ATTACHMENT (CONN           IN OUT NOCOPY UTL_SMTP.CONNECTION
                             , MIME_TYPE      IN            VARCHAR2 DEFAULT 'text/plain'
                             , INLINE         IN            BOOLEAN DEFAULT TRUE
                             , FILENAME       IN            VARCHAR2 DEFAULT NULL
                             , TRANSFER_ENC   IN            VARCHAR2 DEFAULT NULL
                              );

   PROCEDURE END_ATTACHMENT (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION, LAST IN BOOLEAN DEFAULT FALSE );

   PROCEDURE END_MAIL (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION);

   FUNCTION BEGIN_SESSION
      RETURN UTL_SMTP.CONNECTION;

   PROCEDURE BEGIN_MAIL_IN_SESSION (CONN         IN OUT NOCOPY UTL_SMTP.CONNECTION
                                  , SENDER       IN            VARCHAR2
                                  , RECIPIENTS   IN            VARCHAR2
                                  , CC           IN            VARCHAR2 DEFAULT NULL
                                  , BCC          IN            VARCHAR2 DEFAULT NULL
                                  , SUBJECT      IN            VARCHAR2
                                  , MIME_TYPE    IN            VARCHAR2 DEFAULT 'text/plain'
                                  , PRIORITY     IN            PLS_INTEGER DEFAULT NULL
                                   );

   PROCEDURE END_MAIL_IN_SESSION (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION);

   PROCEDURE END_SESSION (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION);

   PROCEDURE SEND_EMAIL (P_DIRECTORY     IN     VARCHAR2
                       , P_SENDER        IN     VARCHAR2
                       , P_RECIPIENT     IN     VARCHAR2
                       , P_CC            IN     VARCHAR2
                       , P_BCC           IN     VARCHAR2
                       , P_SUBJECT       IN     VARCHAR2
                       , P_BODY          IN     VARCHAR2
                       , P_ATTACHMENT1   IN     VARCHAR2 DEFAULT NULL
                       , P_ATTACHMENT2   IN     VARCHAR2 DEFAULT NULL
                       , P_ATTACHMENT3   IN     VARCHAR2 DEFAULT NULL
                       , P_ATTACHMENT4   IN     VARCHAR2 DEFAULT NULL
                       , P_ERROR            OUT VARCHAR2
                        );
END DEMO_MAIL;
/

show errors

/* End of demo_mail_spec.sql */



/*-------------------------------------------------------------------------
----- Package Body: demo_mail
----- File: demo_mail_body.sql
----- Main Routine: send_email(P_SENDER IN VARCHAR2,
---------- P_RECIPIENT IN VARCHAR2,
---------- P_CC IN VARCHAR2,
---------- P_BCC IN VARCHAR2,
---------- P_SUBJECT IN VARCHAR2,
---------- P_BODY IN VARCHAR2,
---------- P_ATTACHMENT1 IN VARCHAR2 default null,
---------- P_ATTACHMENT2 IN VARCHAR2 default null,
---------- P_ATTACHMENT3 IN VARCHAR2 default null,
---------- P_ATTACHMENT4 IN VARCHAR2 default null,
---------- P_ERROR OUT VARCHAR2)
----------
----- Description: This package implements calls to ult_smtp and utl_encode
----- packages. Tje send_mail procedure is the entry point to send an e-mail
----- with upto 4 attachment filenames. The package requires creation of a
----- directory object BFILE_DIR pointing to the absolute patch were the
----- attachment files are stored.
-----
----- SQL: create or replace directory BFILE_DIR as '/home/attachments';
-----
----- Script Filename: setup.sql
-----
----------------------------------------------------------------------------*/


CREATE OR REPLACE PACKAGE BODY DEMO_MAIL IS
   FUNCTION GET_ADDRESS (ADDR_LIST IN OUT VARCHAR2)
      RETURN VARCHAR2 IS
      ADDR   VARCHAR2 (256);
      I      PLS_INTEGER;

      FUNCTION LOOKUP_UNQUOTED_CHAR (STR IN VARCHAR2, CHRS IN VARCHAR2)
         RETURN PLS_INTEGER AS
         C              VARCHAR2 (5);
         I              PLS_INTEGER;
         LEN            PLS_INTEGER;
         INSIDE_QUOTE   BOOLEAN;
      BEGIN
         INSIDE_QUOTE :=   FALSE;
         I :=              1;
         LEN :=            LENGTH (STR);

         WHILE (I <= LEN) LOOP
            C :=   SUBSTR (STR, I, 1);

            IF (INSIDE_QUOTE) THEN
               IF (C = '"') THEN
                  INSIDE_QUOTE :=   FALSE;
               ELSIF (C = '\') THEN
                  I :=   I + 1;                                                              -- Skip the quote character
               END IF;
            END IF;

            IF (C = '"') THEN
               INSIDE_QUOTE :=   TRUE;
            END IF;

            IF (INSTR (CHRS, C) >= 1) THEN
               RETURN I;
            END IF;

            I :=   I + 1;
         END LOOP;

         RETURN 0;
      END;
   BEGIN
      ADDR_LIST :=   LTRIM (ADDR_LIST);
      I :=           LOOKUP_UNQUOTED_CHAR (ADDR_LIST, ',;');

      IF (I >= 1) THEN
         ADDR :=        SUBSTR (ADDR_LIST, 1, I - 1);
         ADDR_LIST :=   SUBSTR (ADDR_LIST, I + 1);
      ELSE
         ADDR :=        ADDR_LIST;
         ADDR_LIST :=   '';
      END IF;

      I :=           LOOKUP_UNQUOTED_CHAR (ADDR, '<');

      IF (I >= 1) THEN
         ADDR :=   SUBSTR (ADDR, I + 1);
         I :=      INSTR (ADDR, '>');

         IF (I >= 1) THEN
            ADDR :=   SUBSTR (ADDR, 1, I - 1);
         END IF;
      END IF;

      RETURN ADDR;
   END;

   PROCEDURE WRITE_MIME_HEADER (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION, NAME IN VARCHAR2, VALUE IN VARCHAR2) IS
   BEGIN
      UTL_SMTP.WRITE_RAW_DATA (CONN, UTL_RAW.CAST_TO_RAW (NAME || ': ' || VALUE || UTL_TCP.CRLF));
   END;

   PROCEDURE WRITE_BOUNDARY (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION, LAST IN BOOLEAN DEFAULT FALSE ) AS
   BEGIN
      IF (LAST) THEN
         UTL_SMTP.WRITE_DATA (CONN, LAST_BOUNDARY);
      ELSE
         UTL_SMTP.WRITE_DATA (CONN, FIRST_BOUNDARY);
      END IF;
   END;

   PROCEDURE MAIL (SENDER       IN VARCHAR2
                 , RECIPIENTS   IN VARCHAR2
                 , CC           IN VARCHAR2 DEFAULT NULL
                 , BCC          IN VARCHAR2 DEFAULT NULL
                 , SUBJECT      IN VARCHAR2
                 , MESSAGE      IN VARCHAR2
                  ) IS
      CONN   UTL_SMTP.CONNECTION;
   BEGIN
      CONN :=
         BEGIN_MAIL (SENDER
                   , RECIPIENTS
                   , CC
                   , BCC
                   , SUBJECT
                    );
      WRITE_TEXT (CONN, MESSAGE);
      END_MAIL (CONN);
   END;

   FUNCTION BEGIN_MAIL (SENDER       IN VARCHAR2
                      , RECIPIENTS   IN VARCHAR2
                      , CC           IN VARCHAR2 DEFAULT NULL
                      , BCC          IN VARCHAR2 DEFAULT NULL
                      , SUBJECT      IN VARCHAR2
                      , MIME_TYPE    IN VARCHAR2 DEFAULT 'text/plain'
                      , PRIORITY     IN PLS_INTEGER DEFAULT NULL
                       )
      RETURN UTL_SMTP.CONNECTION IS
      CONN   UTL_SMTP.CONNECTION;
   BEGIN
      CONN :=   BEGIN_SESSION;
      BEGIN_MAIL_IN_SESSION (CONN
                           , SENDER
                           , RECIPIENTS
                           , CC
                           , BCC
                           , SUBJECT
                           , MIME_TYPE
                           , PRIORITY
                            );
      RETURN CONN;
   END;

   PROCEDURE WRITE_TEXT (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION, MESSAGE IN VARCHAR2) IS
   BEGIN
      UTL_SMTP.WRITE_DATA (CONN, MESSAGE);
   END;

   PROCEDURE WRITE_MB_TEXT (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION, MESSAGE IN VARCHAR2) IS
   BEGIN
      UTL_SMTP.WRITE_RAW_DATA (CONN, UTL_RAW.CAST_TO_RAW (MESSAGE));
   END;

   PROCEDURE WRITE_RAW (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION, MESSAGE IN RAW) IS
   BEGIN
      UTL_SMTP.WRITE_RAW_DATA (CONN, MESSAGE);
   END;

   PROCEDURE ATTACH_TEXT (CONN        IN OUT NOCOPY UTL_SMTP.CONNECTION
                        , DATA        IN            VARCHAR2
                        , MIME_TYPE   IN            VARCHAR2 DEFAULT 'text/plain'
                        , INLINE      IN            BOOLEAN DEFAULT TRUE
                        , FILENAME    IN            VARCHAR2 DEFAULT NULL
                        , LAST        IN            BOOLEAN DEFAULT FALSE
                         ) IS
   BEGIN
      BEGIN_ATTACHMENT (CONN
                      , MIME_TYPE
                      , INLINE
                      , FILENAME
                       );
      WRITE_TEXT (CONN, DATA);
      END_ATTACHMENT (CONN, LAST);
   END;

   PROCEDURE ATTACH_MB_TEXT (CONN        IN OUT NOCOPY UTL_SMTP.CONNECTION
                           , DATA        IN            VARCHAR2
                           , MIME_TYPE   IN            VARCHAR2 DEFAULT 'text/plain'
                           , INLINE      IN            BOOLEAN DEFAULT TRUE
                           , FILENAME    IN            VARCHAR2 DEFAULT NULL
                           , LAST        IN            BOOLEAN DEFAULT FALSE
                            ) IS
   BEGIN
      BEGIN_ATTACHMENT (CONN
                      , MIME_TYPE
                      , INLINE
                      , FILENAME
                       );
      UTL_SMTP.WRITE_RAW_DATA (CONN, UTL_RAW.CAST_TO_RAW (DATA));
      END_ATTACHMENT (CONN, LAST);
   END;

   PROCEDURE ATTACH_BASE64 (CONN        IN OUT NOCOPY UTL_SMTP.CONNECTION
                          , DATA        IN            RAW
                          , MIME_TYPE   IN            VARCHAR2 DEFAULT 'application/octet'
                          , INLINE      IN            BOOLEAN DEFAULT TRUE
                          , FILENAME    IN            VARCHAR2 DEFAULT NULL
                          , LAST        IN            BOOLEAN DEFAULT FALSE
                           ) IS
      I     PLS_INTEGER;
      LEN   PLS_INTEGER;
   BEGIN
      BEGIN_ATTACHMENT (CONN
                      , MIME_TYPE
                      , INLINE
                      , FILENAME
                      , 'base64'
                       );
      I :=     1;
      LEN :=   UTL_RAW.LENGTH (DATA);

      WHILE (I < LEN) LOOP
         IF (I + MAX_BASE64_LINE_WIDTH < LEN) THEN
            UTL_SMTP.WRITE_RAW_DATA (CONN, UTL_ENCODE.BASE64_ENCODE (UTL_RAW.SUBSTR (DATA, I, MAX_BASE64_LINE_WIDTH)));
         ELSE
            UTL_SMTP.WRITE_RAW_DATA (CONN, UTL_ENCODE.BASE64_ENCODE (UTL_RAW.SUBSTR (DATA, I)));
         END IF;

         UTL_SMTP.WRITE_DATA (CONN, UTL_TCP.CRLF);
         I :=   I + MAX_BASE64_LINE_WIDTH;
      END LOOP;

      END_ATTACHMENT (CONN, LAST);
   END;

   PROCEDURE BEGIN_ATTACHMENT (CONN           IN OUT NOCOPY UTL_SMTP.CONNECTION
                             , MIME_TYPE      IN            VARCHAR2 DEFAULT 'text/plain'
                             , INLINE         IN            BOOLEAN DEFAULT TRUE
                             , FILENAME       IN            VARCHAR2 DEFAULT NULL
                             , TRANSFER_ENC   IN            VARCHAR2 DEFAULT NULL
                              ) IS
   BEGIN
      WRITE_BOUNDARY (CONN);
      WRITE_MIME_HEADER (CONN, 'Content-Type', MIME_TYPE);

      IF (FILENAME IS NOT NULL) THEN
         IF (INLINE) THEN
            WRITE_MIME_HEADER (CONN, 'Content-Disposition', 'inline; filename="' || FILENAME || '"');
         ELSE
            WRITE_MIME_HEADER (CONN, 'Content-Disposition', 'attachment; filename="' || FILENAME || '"');
         END IF;
      END IF;

      IF (TRANSFER_ENC IS NOT NULL) THEN
         WRITE_MIME_HEADER (CONN, 'Content-Transfer-Encoding', TRANSFER_ENC);
      END IF;

      UTL_SMTP.WRITE_DATA (CONN, UTL_TCP.CRLF);
   END;

   PROCEDURE END_ATTACHMENT (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
         WRITE_BOUNDARY (CONN, LAST);
      END IF;
   END;

   PROCEDURE END_MAIL (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION) IS
   BEGIN
      END_MAIL_IN_SESSION (CONN);
      END_SESSION (CONN);
   END;

   FUNCTION BEGIN_SESSION
      RETURN UTL_SMTP.CONNECTION IS
      CONN   UTL_SMTP.CONNECTION;
   BEGIN
      CONN :=   UTL_SMTP.OPEN_CONNECTION (SMTP_HOST, SMTP_PORT);
      UTL_SMTP.HELO (CONN, SMTP_DOMAIN);
      RETURN CONN;
   END;

   PROCEDURE BEGIN_MAIL_IN_SESSION (CONN         IN OUT NOCOPY UTL_SMTP.CONNECTION
                                  , SENDER       IN            VARCHAR2
                                  , RECIPIENTS   IN            VARCHAR2
                                  , CC           IN            VARCHAR2 DEFAULT NULL
                                  , BCC          IN            VARCHAR2 DEFAULT NULL
                                  , SUBJECT      IN            VARCHAR2
                                  , MIME_TYPE    IN            VARCHAR2 DEFAULT 'text/plain'
                                  , PRIORITY     IN            PLS_INTEGER DEFAULT NULL
                                   ) IS
      MY_RECIPIENTS   VARCHAR2 (32767) := RECIPIENTS;
      MY_SENDER       VARCHAR2 (32767) := SENDER;
      MY_CC           VARCHAR2 (32767) := CC;
      MY_BCC          VARCHAR2 (32767) := BCC;
   BEGIN
      UTL_SMTP.MAIL (CONN, GET_ADDRESS (MY_SENDER));

      WHILE (MY_RECIPIENTS IS NOT NULL) LOOP
         UTL_SMTP.RCPT (CONN, GET_ADDRESS (MY_RECIPIENTS));
      END LOOP;

      WHILE (MY_CC IS NOT NULL) LOOP
         UTL_SMTP.RCPT (CONN, GET_ADDRESS (MY_CC));
      END LOOP;

      WHILE (MY_BCC IS NOT NULL) LOOP
         UTL_SMTP.RCPT (CONN, GET_ADDRESS (MY_BCC));
      END LOOP;

      UTL_SMTP.OPEN_DATA (CONN);
      WRITE_MIME_HEADER (CONN, 'From', SENDER);
      WRITE_MIME_HEADER (CONN, 'To', RECIPIENTS);
      WRITE_MIME_HEADER (CONN, 'CC', CC);
      WRITE_MIME_HEADER (CONN, 'BCC', BCC);
      WRITE_MIME_HEADER (CONN, 'Subject', SUBJECT);
      WRITE_MIME_HEADER (CONN, 'Content-Type', MIME_TYPE);
      WRITE_MIME_HEADER (CONN, 'X-Mailer', MAILER_ID);

      IF (PRIORITY IS NOT NULL) THEN
         WRITE_MIME_HEADER (CONN, 'X-Priority', PRIORITY);
      END IF;

      UTL_SMTP.WRITE_DATA (CONN, UTL_TCP.CRLF);

      IF (MIME_TYPE LIKE 'multipart/mixed%') THEN
         WRITE_TEXT (CONN, 'This is a multi-part message in MIME format.' || UTL_TCP.CRLF);
      END IF;
   END;

   PROCEDURE END_MAIL_IN_SESSION (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION) IS
   BEGIN
      UTL_SMTP.CLOSE_DATA (CONN);
   END;

   PROCEDURE END_SESSION (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION) IS
   BEGIN
      UTL_SMTP.QUIT (CONN);
   END;

   --------------------------------------------------------------------------------------------------------
   ------------------------------------------- Main email procedure ---------------------------------------
   --------------------------------------------------------------------------------------------------------


   PROCEDURE SEND_EMAIL (P_DIRECTORY     IN     VARCHAR2
                       , P_SENDER        IN     VARCHAR2
                       , P_RECIPIENT     IN     VARCHAR2
                       , P_CC            IN     VARCHAR2
                       , P_BCC           IN     VARCHAR2
                       , P_SUBJECT       IN     VARCHAR2
                       , P_BODY          IN     VARCHAR2
                       , P_ATTACHMENT1   IN     VARCHAR2 DEFAULT NULL
                       , P_ATTACHMENT2   IN     VARCHAR2 DEFAULT NULL
                       , P_ATTACHMENT3   IN     VARCHAR2 DEFAULT NULL
                       , P_ATTACHMENT4   IN     VARCHAR2 DEFAULT NULL
                       , P_ERROR            OUT VARCHAR2
                        ) IS
      FIL                BFILE;
      FILE_LEN           PLS_INTEGER;
      MAX_LINE_WIDTH     PLS_INTEGER := 54;
      BUF                RAW (2100);
      AMT                BINARY_INTEGER := 672 * 3;                                    /* ensures proper format; 2016 */
      POS                PLS_INTEGER := 1;                                                  /* pointer for each piece */
      FILEPOS            PLS_INTEGER := 1;                                                    /* pointer for the file */
      T_FILE1            VARCHAR2 (100) := P_ATTACHMENT1;                                   /* binary file attachment */
      T_FILE2            VARCHAR2 (100) := P_ATTACHMENT2;                                   /* binary file attachment */
      T_FILE3            VARCHAR2 (100) := P_ATTACHMENT3;                                   /* binary file attachment */
      T_FILE4            VARCHAR2 (100) := P_ATTACHMENT4;                                   /* binary file attachment */
      V_FILE_NAME        VARCHAR2 (100) := NULL;                                             /* ascii file attachment */
      V_FILE_HANDLE      UTL_FILE.FILE_TYPE;
      V_DIRECTORY_NAME   VARCHAR2 (100) := P_DIRECTORY;
      V_LINE             VARCHAR2 (2000);
      V_RLINE            RAW (1000);
      CONN               UTL_SMTP.CONNECTION;
      MESG               VARCHAR2 (32767);
      MESG_LEN           NUMBER;
      CRLF               VARCHAR2 (2) := CHR (13) || CHR (10);
      DATA               RAW (2100);
      CHUNKS             PLS_INTEGER;
      LEN                PLS_INTEGER := 1;
      MODULO             PLS_INTEGER;
      PIECES             PLS_INTEGER;
      ERR_NUM            NUMBER;
      ERR_MSG            VARCHAR2 (100);
      --v_mime_type_bin varchar2(30) := 'application/pdf';
      --v_mime_type_bin varchar2(30) := 'application/doc';
      --v_mime_type_bin varchar2(30) := 'application/jpg';


      /* Use this mime type when multiple attachments of different types are sent. */
      V_MIME_TYPE_BIN    VARCHAR2 (30) := 'application/octet-stream';
      -- working storage
      FILES              TAB_OF_ATTACHMENTS;
      T_FILE_COUNT       NUMBER := 0;
   BEGIN
      P_ERROR :=   '0';

      -- put the attachments into the pl/sql table
      IF T_FILE1 IS NOT NULL THEN
         FILES :=                TAB_OF_ATTACHMENTS (NULL);
         FILES (1).FILENAME :=   T_FILE1;
         T_FILE_COUNT :=         1;

         IF T_FILE2 IS NOT NULL THEN
            FILES.EXTEND (1);
            FILES (2).FILENAME :=   T_FILE2;
            T_FILE_COUNT :=         2;
         END IF;

         IF T_FILE3 IS NOT NULL THEN
            FILES.EXTEND (1);
            FILES (3).FILENAME :=   T_FILE3;
            T_FILE_COUNT :=         3;
         END IF;

         IF T_FILE4 IS NOT NULL THEN
            FILES.EXTEND (1);
            FILES (4).FILENAME :=   T_FILE4;
            T_FILE_COUNT :=         4;
         END IF;
      ELSE
         T_FILE_COUNT :=   0;
      END IF;


      BEGIN
         CONN :=
            DEMO_MAIL.BEGIN_MAIL (SENDER => P_SENDER
                                , RECIPIENTS => P_RECIPIENT
                                , CC =>     P_CC
                                , BCC =>    P_BCC
                                , SUBJECT => P_SUBJECT
                                , MIME_TYPE => DEMO_MAIL.MULTIPART_MIME_TYPE
                                 );
      END BEGIN_MAIL;

      BEGIN
         DEMO_MAIL.ATTACH_TEXT (CONN => CONN, DATA => P_BODY || CRLF || CRLF, MIME_TYPE => 'text/html');
      END ATTACH_TEXT;


      BEGIN
         -- check to see if there are any attachments (otherwise the loop will error!)
         IF T_FILE_COUNT > 0 THEN
            -- loop through attachments
            FOR I IN 1 .. FILES.COUNT LOOP
               DEMO_MAIL.BEGIN_ATTACHMENT (CONN =>   CONN
                                         , MIME_TYPE => V_MIME_TYPE_BIN
                                         , INLINE => TRUE
                                         , FILENAME => FILES (I).FILENAME
                                         , TRANSFER_ENC => 'base64'
                                          );

               BEGIN
                  FILEPOS :=    1;                                   /* Insures we are pointing to beginning of file. */
                  AMT :=        672 * 3;                             /* Insures amount is re-initialize for each file */
                  FIL :=        BFILENAME (V_DIRECTORY_NAME, FILES (I).FILENAME);
                  FILE_LEN :=   DBMS_LOB.GETLENGTH (FIL);
                  MODULO :=     MOD (FILE_LEN, AMT);

                  PIECES :=     TRUNC (FILE_LEN / AMT);

                  IF (MODULO <> 0) THEN
                     PIECES :=   PIECES + 1;
                  END IF;

                  DBMS_LOB.FILEOPEN (FIL, DBMS_LOB.FILE_READONLY);
                  DBMS_LOB.READ (FIL
                               , AMT
                               , FILEPOS
                               , BUF
                                );
                  DATA :=       NULL;

                  FOR I IN 1 .. PIECES LOOP
                     FILEPOS :=    I * AMT + 1;
                     FILE_LEN :=   FILE_LEN - AMT;
                     DATA :=       UTL_RAW.CONCAT (DATA, BUF);
                     CHUNKS :=     TRUNC (UTL_RAW.LENGTH (DATA) / MAX_LINE_WIDTH);

                     IF (I <> PIECES) THEN
                        CHUNKS :=   CHUNKS - 1;
                     END IF;

                     DEMO_MAIL.WRITE_RAW (CONN => CONN, MESSAGE => UTL_ENCODE.BASE64_ENCODE (DATA));
                     DATA :=       NULL;

                     IF (FILE_LEN < AMT
                     AND FILE_LEN > 0) THEN
                        AMT :=   FILE_LEN;
                     END IF;

                     /* Insures we only read again if there is more data. */
                     /*Commented and changed on 18-Apr-2008*/

                     --if (file_len > amt) then
                     --dbms_lob.read(fil, amt, filepos, buf);
                     --end if;
                     ---Changed code----------

                     IF (FILE_LEN = 0) THEN
                        NULL;
                     ELSE
                        DBMS_LOB.READ (FIL
                                     , AMT
                                     , FILEPOS
                                     , BUF
                                      );
                     END IF;
                  -----------------------------
                  END LOOP;
               END;

               DBMS_LOB.FILECLOSE (FIL);
               DEMO_MAIL.END_ATTACHMENT (CONN => CONN);
            END LOOP;
         END IF;
      END BEGIN_ATTACHMENT;

      DEMO_MAIL.END_MAIL (CONN => CONN);
   EXCEPTION
      WHEN NO_DATA_FOUND THEN
         DEMO_MAIL.END_ATTACHMENT (CONN => CONN);
         DBMS_LOB.FILECLOSE (FIL);
         P_ERROR :=   'Error: No data found.';
      WHEN OTHERS THEN
         DEMO_MAIL.END_ATTACHMENT (CONN => CONN);
         ERR_NUM :=   SQLCODE;
         ERR_MSG :=   SUBSTR (SQLERRM, 1, 100);
         P_ERROR :=   'Error: ' || ERR_NUM || ' - ' || ERR_MSG;
         DBMS_OUTPUT.PUT_LINE ('Error number is ' || ERR_NUM);
         DBMS_OUTPUT.PUT_LINE ('Error message is ' || ERR_MSG);
         DBMS_LOB.FILECLOSE (FIL);
   END;
END DEMO_MAIL;
/

/******************End of package body for the E-mail program*****************/

/*-- A simple way of sending upto 4 attachments --*/
---simple.sql---


DECLARE
   PO_ERR_MSG   VARCHAR2 (1000);
BEGIN
   DEMO_MAIL.SEND_EMAIL (P_DIRECTORY => 'BFILE_DIR'                                 --replace with your directory object
                       , P_SENDER => 'sender@somedomain.com'
                       , P_RECIPIENT => 'receiver@somedomain.com'
                       , P_CC =>   'first.last@somedomain.com'
                       , P_BCC =>  NULL
                       , P_SUBJECT => 'Subject of the e-mail '
                       , P_BODY => 'Please find the following n attachments.'
                       , P_ATTACHMENT1 => 'blob.jpg'                    --replace with your attachment filename or null.
                       , P_ATTACHMENT2 => 'test.doc'                     --replace with your attachment filename or null
                       , P_ATTACHMENT3 => 'text.txt'                     --replace with your attachment filename or null
                       , P_ATTACHMENT4 => 'mypdf.pdf'                    --replace with your attachment filename or null
                       , P_ERROR => PO_ERR_MSG                                                           -- should be 0.
                        );
   DBMS_OUTPUT.PUT_LINE ('THIS IS THE ERROR: ' || PO_ERR_MSG);
END;
/

/*---End of simple.sql------*/

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 Aloz 的頭像
    Aloz

    Aloz 的 Oracle ERP 天地

    Aloz 發表在 痞客邦 留言(0) 人氣()