您的位置:首页 > 数据库 > Oracle

Oracle发送邮件,支持HTML,多收件人,多附件

2015-07-02 17:20 555 查看
Oracle发邮件,权限问题

- 创建 ACL

BEGIN

DBMS_NETWORK_ACL_ADMIN.CREATE_ACL(acl => 'email_server_permissions.xml',

description => 'Enables network permissions for the e-mail server',

principal => 'LJZ',

is_grant => TRUE,

privilege => 'connect');

END;

-- 与邮件服务关联

BEGIN

DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL(acl => 'email_server_permissions.xml',

host => 'smtp.163.com',

lower_port => 25,

upper_port => NULL);

END;

-- 这样 email_user 用户帐户创建的存储过程便可以向此邮件服务器发送邮件

       
 

--删除

BEGIN

DBMS_NETWORK_ACL_ADMIN.drop_acl(acl => 'email_server_permissions.xml');

COMMIT;

END;

--查询

SELECT host, lower_port, upper_port, acl FROM dba_network_acls;

SELECT acl,

principal,

privilege,

is_grant,

TO_CHAR(start_date, 'DD-MON-YYYY') AS start_date,

TO_CHAR(end_date, 'DD-MON-YYYY') AS end_date

FROM dba_network_acl_privileges;

存储过程

 SQL Code 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319

   

create or replace procedure send_mail_sp(p_from            in varchar2,
                                         p_to              in varchar2,
                                         p_subject         in varchar2,
                                         p_text            in clob default null,
                                         p_html            in clob default null,
                                         p_attachment_path varchar2,
                                         p_smtp_hostname   in varchar2,
                                         p_smtp_portnum    in varchar2 default 25,
                                         p_need_valid      varchar2 default 'Y',
                                         p_user_name       varchar2,
                                         p_user_pwd        varchar2) IS
  /*
  作用:用oracle发送邮件
  主要功能:1、支持多收件人。
            2、支持中文
            3、支持抄送人
            4、支持大于32K的附件
            5、支持多行正文
            6、支持多附件
            7、支持HTML邮件
  作者:jinzhao
  参数说明:
            p_from            发件人
            p_to              收件人,多收件人用逗号或分号分隔
            p_subject         主题
            p_text            文本内容
            p_html            HTML内容
            p_attachment_path 附件地址(绝对路径),多附件用逗号或分号分隔
            p_smtp_hostname   邮件服务器地址 例:smtp.163.com
            p_smtp_portnum    端口号,默认25
            p_need_valid      是否需要用户名密码验证,默认需要('Y')
            p_user_name       用户名
            p_user_pwd        密码
  */
  l_crlf                   varchar2(2) := utl_tcp.crlf;
  l_write_encode           varchar2(20) := 'zhs16gbk';
  l_attachment_encode      varchar2(10) := 'base64';
  l_attachment_mime_type   varchar2(50) := 'application/octet-stream';
  l_attachment_disposition varchar2(10) := 'attachment';
  l_boundary_mail          varchar2(255) default '#---hhiuuihh-mail---#';
  l_boundary_content       varchar2(255) default '#---hhiuuihh-content---#';
  l_first_boundary constant varchar2(256) := '--' || l_boundary_mail ||
                                             l_crlf;
  l_last_boundary  constant varchar2(256) := '--' || l_boundary_mail || '--' ||
                                             l_crlf;
  l_connection          utl_smtp.connection;
  l_body_html           clob := empty_clob; --HTML邮件内容
  l_offset              number;
  l_ammount             number;
  l_temp                varchar2(32767) default null;
  l_file_handle         utl_file.file_type;
  l_line                varchar2(1000);
  l_mesg                varchar2(32767);
  l_filepos             pls_integer := 1;
  l_fil                 bfile;
  l_file_len            number;
  l_modulo              number;
  l_pieces              number;
  l_amt                 number := 8580;
  l_chunks              number;
  l_buf                 raw(32767);
  l_data                raw(32767);
  l_max_line_width      number := 54;
  l_directory_base_name varchar2(100) := 'DIR_FOR_SEND_MAIL';
  l_receivers           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;

  --分割邮件地址或者附件地址
  procedure sub_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;
  --删除directory
  procedure sub_drop_directory(p_directory_name varchar2) is
  begin
    execute immediate 'drop directory ' || p_directory_name;
  exception
    when others then
      null;
  end;
  --创建directory
  procedure sub_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;
  --返回附件源文件所在目录或者名称
  function sub_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
      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
      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;
  --发送附件
  procedure sub_attachment(conn     in out nocopy utl_smtp.connection,
                           filename in varchar2,
                           dt_name  in varchar2) is
  
    l_filename varchar2(1000);
    l_amount   number;
  begin
    sub_drop_directory(dt_name);
    --创建directory
    sub_create_directory(dt_name, sub_get_file(filename, 1));
    --得到附件文件名称
    l_filename := sub_get_file(filename, 2);
    l_temp     := l_temp || l_crlf || '--' || l_boundary_mail || l_crlf;
    l_temp     := l_temp || 'Content-Type: ' || l_attachment_mime_type || ';
    name="' || l_filename || '"
Content-Transfer-Encoding: ' || l_attachment_encode || '
Content-Disposition: ' || l_attachment_disposition || ';
    filename="' || l_filename || '"' || l_crlf || l_crlf;
    utl_smtp.write_raw_data(l_connection,
                            utl_raw.cast_to_raw(convert(l_temp,
                                                        l_write_encode)));
    --begin
    --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);
    l_data   := null;
    l_amount := l_amt;
    for i in 1 .. l_pieces loop
      dbms_lob.read(l_fil, l_amount, l_filepos, l_buf);
      l_filepos  := i * l_amount + 1;
      l_file_len := l_file_len - l_amount;
      utl_smtp.write_raw_data(conn, utl_encode.base64_encode(l_buf));
      if i = l_pieces then
        l_amount := l_file_len;
      end if;
    end loop;
    dbms_lob.fileclose(l_fil);
    /*exception
      when others then
        dbms_lob.fileclose(l_fil);
        sub_end_boundary(conn);
        raise;
    end; --结束处理二进制附件*/
    sub_drop_directory(dt_name);
  end; --结束过程attachment
  procedure sub_send_mail is
    l_from varchar2(1000) := '<' || p_from || '>';
  begin
    l_connection := utl_smtp.open_connection(p_smtp_hostname,
                                             p_smtp_portnum);
    utl_smtp.helo(l_connection, p_smtp_hostname);
  
    /* smtp服务器登录校验 */
    IF p_need_valid = 'Y' THEN
      utl_smtp.command(l_connection, 'AUTH LOGIN', '');
      utl_smtp.command(l_connection,
                       utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(p_user_name))));
      utl_smtp.command(l_connection,
                       utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(p_user_pwd))));
    END IF;
    utl_smtp.mail(l_connection, l_from);
    sub_splite_str(p_to); --处理邮件地址
    for k in 1 .. my_address_list.count loop
      l_receivers := l_receivers || my_address_list(k) || ';';
      utl_smtp.rcpt(l_connection, my_address_list(k));
    end loop;
    l_temp := l_temp || 'From: ' || l_from || l_crlf;
    l_temp := l_temp || 'To: ' || l_receivers || l_crlf;
    --l_temp := l_temp || 'Cc: ' || l_receivers || l_crlf;--抄送
    --l_temp := l_temp || 'Bcc: ' || l_receivers || l_crlf;--密送
    l_temp := l_temp || 'Subject: ' || p_subject || l_crlf;
    --l_temp := l_temp || 'X-Mailer: Foxmail 7, 1, 3, 48[cn]' || l_crlf;--发送客户端
    l_temp := l_temp || 'Content-Type: multipart/mixed; boundary="' ||
              l_boundary_mail || '"' || l_crlf;
    l_temp := l_temp || 'MIME-Version: 1.0' || l_crlf || l_crlf;
    l_temp := l_temp || '--' || l_boundary_mail || l_crlf;
    if nvl(p_attachment_path, ' ') <> ' ' then
      l_temp := l_temp || 'content-type: multipart/alternative; boundary="' ||
                l_boundary_content || '"' || l_crlf || l_crlf || l_crlf;
      l_temp := l_temp || '--' || l_boundary_content || l_crlf;
    end if;
    --开始
    dbms_lob.createtemporary(l_body_html, false, 10);
    dbms_lob.write(l_body_html, length(l_temp), 1, l_temp);
    --文本内容
    l_offset := dbms_lob.getlength(l_body_html) + 1;
    l_temp   := 'content-type: text/plain; charset="GB2312"; Content-Transfer-Encoding: base64' ||
                l_crlf || l_crlf;
    dbms_lob.write(l_body_html, length(l_temp), l_offset, l_temp);
    dbms_lob.append(l_body_html, p_text);
    --html内容
    if nvl(p_attachment_path, ' ') <> ' ' then
      l_temp := l_crlf || l_crlf || '--' || l_boundary_content || l_crlf;
    else
      l_temp := l_crlf || l_crlf || '--' || l_boundary_mail || l_crlf;
    end if;
    l_temp   := l_temp ||
                'content-type: text/html;charset="GB2312";Content-Transfer-Encoding: quoted-printable' ||
                l_crlf || l_crlf;
    l_offset := dbms_lob.getlength(l_body_html) + 1;
    dbms_lob.write(l_body_html, length(l_temp), l_offset, l_temp);
    dbms_lob.append(l_body_html, nvl(p_html, ' '));
    --content结束
    if nvl(p_attachment_path, ' ') <> ' ' then
      l_temp := l_crlf || l_crlf || '--' || l_boundary_content || '--' ||
                l_crlf || l_crlf;
    else
      l_temp := l_crlf || '--' || l_boundary_mail || '--' || l_crlf ||
                l_crlf;
    end if;
    l_offset := dbms_lob.getlength(l_body_html) + 1;
    dbms_lob.write(l_body_html, length(l_temp), l_offset, l_temp);
    --写入邮件
  
    l_offset  := 1;
    l_ammount := 1900;
    utl_smtp.open_data(l_connection);
    while l_offset < dbms_lob.getlength(l_body_html) loop
      utl_smtp.write_raw_data(l_connection,
                              utl_raw.cast_to_raw(convert(dbms_lob.substr(l_body_html,
                                                                          l_ammount,
                                                                          l_offset),
                                                          l_write_encode)));
      l_offset  := l_offset + l_ammount;
      l_ammount := least(1900, dbms_lob.getlength(l_body_html) - l_ammount);
    end loop;
    commit;
    ----------------------------------------------------
    l_temp := null;
    --附件
    --如果文件名称不为空,则发送附件
    if (p_attachment_path is not null) then
      --根据逗号或者分号拆分附件地址
      sub_splite_str(p_attachment_path, 2);
      --循环发送附件(在同一个邮件中)
      for k in 1 .. my_acct_list.count loop
        sub_attachment(conn     => l_connection,
                       filename => my_acct_list(k),
                       dt_name  => l_directory_base_name || to_char(k));
        l_temp := l_crlf;
      end loop;
    end if;
    l_temp := l_crlf || l_crlf || '--' || l_boundary_mail || '--' || l_crlf ||
              l_crlf;
    utl_smtp.write_raw_data(l_connection,
                            utl_raw.cast_to_raw(convert(l_temp,
                                                        l_write_encode)));
    commit;
    utl_smtp.close_data(l_connection);
    utl_smtp.quit(l_connection);
    --utl_smtp.close_connection(l_connection);
    dbms_lob.freetemporary(l_body_html);
  end;
begin
  sub_send_mail();
  /*exception
  when others then
  null;*/
end;

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: