修正oracle 发送带附件邮件(smtp)无法打开,并增加报表改名发送

原文地址:https://blog.csdn.net/zhanzhib/article/details/42292455

经测试,好像单用户发送没问题附件没问题,多用户发送只有第一个用户能正常打开附件。(PS格式,不完美)

2018/07/27 最终解决了多用户发问题,原程序有几个小问题,更新!

发邮件进程:

CREATE OR REPLACE Procedure NI_TEST_EMAIL(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格式

     参数说明:
               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:\temp\a.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) := '-----By ERP';
     FIRST_BOUNDARY Constant Varchar2(256) := '--' || BOUNDARY || L_CRLF;
     LAST_BOUNDARY Constant Varchar2(256) := '--' || BOUNDARY || '--' || L_CRLF;
		 --2018/07/27 增加charset=gb2312; 解决部分系统标题乱码
     MULTIPART_MIME_TYPE Constant Varchar2(256) := 'multipart/mixed; charset=gb2312;boundary="' ||BOUNDARY || '"';
     /* 以下部分是发送大二进制附件时用到的变量 */
     L_FIL Bfile;
     L_FILE_LEN Number;
     --L_MODULO Number;
     L_PIECES Number;
     L_FILE_HANDLE UTL_FILE.FILE_TYPE;
		 --2018/07/27 屏蔽员 L_AMT:=672 * 3的赋值,是不能多用户发送的元凶,再写附件时初始化
     L_AMT Binary_Integer; /* 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
                    L_AMT := 672 * 3;----2018/07/27 增加 初始化
                    --把附件分成多份,这样可以发送超过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 := Ceil(L_FILE_LEN / L_AMT);--2018/07/26 附件打不开修改 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有可能会提示“ORA-29279: SMTP 永久性错误: 503 5.5.2 Send hello first.”
          --改成使用UTL_SMTP.EHLO就好了
          --UTL_SMTP.HELO(L_CONN, P_SERVER);
          UTL_SMTP.EHLO(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);
          --2018/07/26 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,
                               INLINE => false,
                               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;

报表附件发送前修改后缀,原报表后缀是.out,修改为.ps(.xls也可以)

Declare
     p_Request_Id Number := 19779753;--报表ID
     v_email_address Varchar2(200) := '[email protected]';
     Cursor c Is
          Select a.Request_Id       Request_Id,
                 b.Outfile_Name     Outfile_Name,
                 a.Status_Code      Status_Code,
                 a.Phase_Code       Phase_Code,
                 b.Output_File_Type,
                 a.Program
            From Fnd_Conc_Req_Summary_v a, Fnd_Concurrent_Requests b
           Where a.Request_Id = b.Request_Id
                 And a.Request_Id = p_Request_Id
                 And a.Phase_Code = 'C'
                 And a.Status_Code = 'C'
                 And b.Outfile_Name Is Not Null;
   
     ln_last_split   Number;
     v_src_location  Varchar2(300);
     v_file_name     Varchar2(100);
     v_dest_filename Varchar2(100);
Begin
     For C1 In c Loop
          ln_last_split := instr(C1.Outfile_Name, '/', -1, 1);
          --原名
          v_file_name := substr(C1.Outfile_Name, ln_last_split + 1);
          --转名后(改ps后缀)
          v_dest_filename := substr(v_file_name,1,instr(v_file_name, '.', -1, 1) ) ||'ps';
          --最终文件路径
          v_src_location := substr(C1.Outfile_Name, 1, ln_last_split) || v_dest_filename;
          utl_file.fcopy('REPORT_DIR', --(需手动创建)环境变量路径 create directory REPORT_DIR as 路径地址; 
                         v_file_name, --原文件名
                         'REPORT_DIR', --(需手动创建)环境变量路径 create directory REPORT_DIR as 路径地址;
                         v_dest_filename --拷贝后文件名
                         );
          NI_TEST_EMAIL(C1.program || 'output file',
                        C1.program,
                        '[email protected]',
                        v_email_address,
                        'smtp1.email.com',
                        25,
                        0,
                        Null,
                        Null,
                        v_src_location,
                        'base64');
     End Loop;
End;

(此问题已解决,保留当其他方式)同时发给多个EMAIL用户,附件有打不开的情况,分拆成单个发送又没问题。

分拆方法,下文中出现ni_app_ext.split()代码:

--先创建数组
CREATE OR REPLACE TYPE NI_STR_ARRAY is VARRAY(10000) OF VARCHAR2(500)
--分拆函数
Function Split(v_source Varchar2, v_delimiter Varchar2 Default ',')
     Return NI_STR_ARRAY Is
     l_DelLen Number;
     l_Pos    Number;
     l_Start  Number;
     l_Length Number;
     l_holder Varchar2(200);
     l_Array NI_STR_ARRAY := ni_str_array();
Begin
     --Check for NULL
     If v_source Is Null
        Or v_delimiter Is Null Then
          l_Array.Extend;
          l_Array(l_Array.Count) := '';
          Return l_Array;
     End If;
     --Get the length of the delimeter
     l_DelLen := Length(v_Delimiter);
     l_Pos := instr(Upper(v_source), Upper(v_Delimiter));
     --Only one entry was found
     If l_Pos = 0 Then
          l_Array.extend;
          l_Array(l_Array.Count) := v_source;
          Return l_Array;
     End If;
     --More than one entry was found - loop to get all of them
     l_Start := 1;
     While l_Pos > 0 Loop
          --Set current entry
          l_Length := l_Pos - l_Start;
          l_holder := substr(v_source, l_start, l_length);
          -- Update array and counter
          l_Array.extend;
          l_Array(l_Array.Count) := l_holder;
          --Set the new starting position
          l_Start := l_Pos + l_DelLen;
          l_Pos := instr(Upper(v_source), Upper(v_Delimiter), l_Start);
     End Loop;
     --Set last entry
     l_holder := substr(v_source, l_start, Length(v_source));
     -- Update array and counter if necessary
     If Length(l_holder) > 0 Then
          l_Array.extend;
          l_Array(l_Array.Count) := l_holder;
     End If;
     --Return the number of entries found
     Return l_Array;
End;

循环分拆发送:

Declare
     p_Request_Id Number := 19781729;
     v_email_address Varchar2(1000) := '[email protected];[email protected];[email protected]';
     Cursor c Is
          Select a.Request_Id       Request_Id,
                 b.Outfile_Name     Outfile_Name,
                 a.Status_Code      Status_Code,
                 a.Phase_Code       Phase_Code,
                 b.Output_File_Type,
                 a.Program
            From Fnd_Conc_Req_Summary_v a, Fnd_Concurrent_Requests b
           Where a.Request_Id = b.Request_Id
                 And a.Request_Id = p_Request_Id
                 And a.Phase_Code = 'C'
                 And a.Status_Code = 'C'
                 And b.Outfile_Name Is Not Null;
     
     ln_last_split   Number;
     v_src_location  Varchar2(300);
     v_file_name     Varchar2(100);
     v_dest_filename Varchar2(100);
     v_one_email Varchar2(100);
Begin
     For C1 In c Loop
          ln_last_split := instr(C1.Outfile_Name, '/', -1, 1);
          --原名
          v_file_name := substr(C1.Outfile_Name, ln_last_split + 1);
          --转名后(改ps后缀)
          v_dest_filename := substr(v_file_name,1,instr(v_file_name, '.', -1, 1) ) ||'xls';
          --最终文件路径
          v_src_location := substr(C1.Outfile_Name, 1, ln_last_split) || v_dest_filename;
          utl_file.fcopy('REPORT_DIR', --环境变量路径 DIRECTORY_NAME
                         v_file_name, --原文件名
                         'REPORT_DIR', --环境变量路径 DIRECTORY_NAME
                         v_dest_filename --拷贝后文件名
                         );
          --循环发邮件开始
          For i In 1 .. ni_app_ext.split(v_email_address,';').count Loop
            v_one_email:=null;
            v_one_email:=ni_app_ext.split(v_email_address, ';') (i);
            If v_one_email Is Not Null Then
              NI_TEST_EMAIL(C1.program || '附件!'||chr(10)||'如出现无法打开现象,请自行到ERP中获取,报表ID:'||p_Request_Id,
                            C1.program,
                            '[email protected]',
                            v_one_email,
                            'smtp1.email.com',
                            25,
                            0,
                            Null,
                            Null,
                            v_src_location,
                            'base64');
             End If;
          End Loop;              
          --循环发邮件结束
     End Loop;
End;

 

你可能感兴趣的:(Oracle,PLSQL,PLSQL发电邮,PLSQL附件电邮)