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;
使用实例:
代码:


sql>set serverout on

sql> exec procsendemail('中文测试邮件','中文主题','space6212@163.com','space6212@163.com

,susk@souchang.com','202.108.5.85',25,1,'xxxx','xxxx','/tmp/a.jpg,/tmp/b.jpg','base64');


pl/sql procedure successfully completed.
(0)
上一篇 2022年3月22日
下一篇 2022年3月22日

相关推荐