Oracle 存储过程发送邮件

CREATE OR REPLACE PROCEDURE PROCSENDEMAIL(P_TXT       VARCHAR2,

                                          P_SUB       VARCHAR2,

                                          P_SENDOR    VARCHAR2,

                                          P_RECEIVER  VARCHAR2,

                                          P_SERVER    VARCHAR2,

                                          P_PORT      NUMBER DEFAULT 25,

                                          P_NEED_SMTP INT DEFAULT 0,

                                          P_USER      VARCHAR2 DEFAULT NULL,

                                          P_PASS      VARCHAR2 DEFAULT NULL,

                                          P_FILENAME  VARCHAR2 DEFAULT NULL,

                                          P_ENCODE    VARCHAR2 DEFAULT ‘bit 7‘)

  AUTHID CURRENT_USER IS

  /*

  作用:用oracle发送邮件

  主要功能:1、支持多收件人。

            2、支持中文

            3、支持抄送人

            4、支持大于32K的附件

            5、支持多行正文

            6、支持多附件

            7、支持文本附件和二进制附件

            8、支持HTML格式

            8、支持

  作者:suk

  参数说明:

            p_txt :邮件正文

            p_sub: 邮件标题

            p_SendorAddress : 发送人邮件地址

            p_ReceiverAddress : 接收地址,可以同时发送到多个地址上,地址之间用","或者";"隔开

            p_EmailServer : 邮件服务器地址,可以是域名或者IP

            p_Port :邮件服务器端口

            p_need_smtp:是否需要smtp认证,0表示不需要,1表示需要

            p_user:smtp验证需要的用户名

            p_pass:smtp验证需要的密码

            p_filename:附件名称,必须包含完整的路径,如"d:tempa.txt"。

                        可以有多个附件,附件名称只见用逗号或者分号分隔

            p_encode:附件编码转换格式,其中 p_encode=‘bit 7‘ 表示文本类型附件

                                             p_encode=‘base64‘ 表示二进制类型附件

  注意:

        1、对于文本类型的附件,不能用base64的方式发送,否则出错

        2、对于多个附件只能用同一种格式发送

  */

  L_CRLF          VARCHAR2(2) := UTL_TCP.CRLF;

  L_SENDORADDRESS VARCHAR2(4000);

  L_SPLITE        VARCHAR2(10) := ‘++‘;

  BOUNDARY            CONSTANT VARCHAR2(256) := ‘-----BYSUK‘;

  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);

  /* 以上部分是发送大二进制附件时用到的变量 */

  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(P_SENDORADDRESS2   VARCHAR2, --发送地址

                    P_RECEIVERADDRESS2 VARCHAR2) --接受地址

   IS

    L_CONN UTL_SMTP.CONNECTION; --定义连接

  BEGIN

    /*初始化邮件服务器信息,连接邮件服务器*/

    L_CONN := UTL_SMTP.OPEN_CONNECTION(P_SERVER, P_PORT);

    UTL_SMTP.HELO(L_CONN, P_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(P_USER))));

      UTL_SMTP.COMMAND(L_CONN,

                       UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(P_PASS))));

    END IF;

    /*设置发送地址和接收地址*/

    UTL_SMTP.MAIL(L_CONN, P_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‘, P_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

  L_SENDORADDRESS := ‘<‘ || P_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;

转:http://www.cnblogs.com/IcefishBingqing/archive/2013/03/05/2944455.html

  

PROCSENDEMAIL(‘邮件内容‘,‘邮件主题‘,‘发件人‘,‘收件人‘,‘邮件服务器ip地址‘,25,1,‘用户名‘,‘密码‘,‘‘,‘base64‘);

注意邮件服务器一定要用ip,用域名不行。

时间: 2024-10-11 23:12:27

Oracle 存储过程发送邮件的相关文章

oracle存储过程实例

oracle存储过程实例 分类: 数据(仓)库及处理 2010-05-03 17:15 1055人阅读 评论(2)收藏 举报 认识存储过程和函数 存储过程和函数也是一种PL/SQL块,是存入数据库的PL/SQL块.但存储过程和函数不同于已经介绍过的PL/SQL程序,我们通常把PL/SQL程序称为无名块,而存储过程和函数是以命名的方式存储于数据库中的.和PL/SQL程序相比,存储过程有非常多长处,详细归纳例如以下: * 存储过程和函数以命名的数据库对象形式存储于数据库其中.存储在数据库中的长处是非

java怎样将一组对象传入Oracle存储过程

java怎样将一组对象传入Oracle存储过程.须要注意的是jar包是有要求的,假设使用不当会导致仅仅有数字能传过去,字符串传只是去.假设是Oracle11g则须要选用例如以下的jar包,F:\app\Administrator\product\11.2.0\dbhome_1\jlib\orai18n.jar. D:\program\weblogic\oracle_common\modules\oracle.jdbc_11.2.0\ojdbc6.jar 样例例如以下: CREATE OR REP

springMVC + oracle存储过程 构建高性能灵活易维护的java web架构

MVC让简单的业务也变得复杂 不知道你在使用MVC模式开发项目的时候是否觉得很麻烦,一个简单的增删改查动作却要涉及到多个文件操作. 以一条数据增加为例说明. 假设我们使用hibernate并且dao层是已经封装好的 从图中可以看出如果我们在系统中写一个增加功能需要自己动手写的地方至少是 jsp , action , service,servicesImpl 四层. 如果是复杂的添加操作那么我们很可能还会自己定义dao层的接口和方法,那样就是6层操作了. 如果使用mybatis 至少也是写4层,常

Oracle 存储过程

存储过程:就是一组用于完成特定数据库功能的SQL 语句集,该SQL语句集经过编译后存储在数据库系统中.在使用时候,用户通过指定已经定义的存储过程名字并给出相应的存储过程参数来调用并执行它,从而完成一个或一系列的数据库操作. Oracle存储过程包含三部分:过程声明,执行过程部分,存储过程异常. 1 create or replace procedure 过程名 2 as ...; --声明语句段 3 begin 4 ...; --执行语句段 5 exception --异常处理语句段 6 ...

Oracle 存储过程定义和优点及与函数区别

定义: 存储过程(Stored Procedure )是一组为了完成特定功能的SQL 语句集,经编译后存储在数据库中.用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它.存储过程是数据库中的一个重要对象,任何一个设计良好的数据库应用程序都应该用到存储过程. 存储过程是由流控制和SQL 语句书写的过程,这个过程经编译和优化后存储在数据库服务器中,应用程序使用时只要调用即可.在ORACLE 中,若干个有联系的过程可以组合在一起构成程序包. 优 点: 1.(预编译)存储过程只在创造

jdbc调用 oracle 存储过程操作

创建有参存储函数findEmpNameAndSal(编号),查询7902号员工的的姓名和月薪,[返回多个值,演示out的用法]当返回2个或多个值,必须使用out符号当返回1个值,就无需out符号 create or replace function findEmpNameAndSal(pempno in number,pename out varchar2) return numberas psal emp.sal%type;begin select ename,sal into pename,

Oracle ——存储过程

1.带参数的存储过程CREATE OR REPLACE PROCEDURE sp_pro2 (spname VARCHAR2, newsal NUMBER) ISBEGIN     UPDATE emp SET sal = newsal WHERE ename=spname;END; --参数VARCHAR2不需要指定大小exec sp_pro2('SMITH',10);或者call sp_pro2('SMITH',10); Oracle --存储过程,布布扣,bubuko.com

面试概率极大的Oracle存储过程

1.什么是存储过程.存储过程是数据库服务器端的一段程序,它有两种类型.一种类似于SELECT查询,用于检索数据,检索到的数据能够以数据集的形式返回给客户.另一种类似于INSERT或DELETE查询,它不返回数据,只是执行一个动作.有的服务器允许同一个存储过程既可以返回数据又可以执行动作.2.什么时候需要用存储过程 如果服务器定义了存储过程,应当根据需要决定是否要用存储过程.存储过程通常是一些经常要执行的任务,这些任务往往是针对大量的记录而进行的.在服务器上执行存储过程,可以改善应用程序的性能.这

用java调用oracle存储过程总结(转)

//1.call+包名+存储过程名(传入.传出值用?) String str="{call SMSBUSINESS.deleteZhZMember(?,?,?)}"; //2.建立连接 Connection conn=null; conn=DriverManager.getConnection(); //3.使用java.sql.*类 CallableStatement cs=conn.prepareCall(str); //4.传入in值 cs.setInt(1,id); cs.se