码迷,mamicode.com
首页 > 数据库 > 详细

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

时间:2015-07-02 19:13:10      阅读:497      评论:0      收藏:0      [点我收藏+]

标签:

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
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363

  

CREATE OR REPLACE PROCEDURE send_mail_sp(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(100INDEX BY BINARY_INTEGER;
  MY_ADDRESS_LIST ADDRESS_LIST;
  TYPE ACCT_LIST IS TABLE OF VARCHAR2(100INDEX BY BINARY_INTEGER;
  MY_ACCT_LIST ACCT_LIST;
  -------------------------------------
返回附件源文件所在目录或者名称---------------------
  FUNCTION GET_FILE(P_FILE VARCHAR2, P_GET INTRETURN 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, 1INSTR(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, 1INSTR(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 VARCHAR2IS
  BEGIN
    EXECUTE IMMEDIATE ‘drop directory ‘ || P_DIRECTORY_NAME;
  EXCEPTION
    WHEN OTHERS THEN
      NULL;
  END;
  --------------------------------------------------
创建directory-----------------------------
  PROCEDURE CREATE_DIRECTORY(P_DIRECTORY_NAME VARCHAR2, P_DIR VARCHAR2IS
  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 1IS
    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 FALSEIS
  BEGIN
    UTL_SMTP.WRITE_DATA(CONN, UTL_TCP.CRLF);
    IF (LASTTHEN
      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 NULLTHEN
      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 <> 0THEN
          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 > 0THEN
            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/html;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 NULLTHEN
      --
根据逗号或者分号拆分附件地址
      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;
    --send_mail_sp(‘
邮件内容(html‘,‘邮件主题‘,‘发件人‘,‘收件人‘,‘邮件服务器ip地址‘,25,1,‘用户名‘,‘密码‘,‘附件绝对地址‘,‘base64‘);
END;

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

标签:

原文地址:http://www.cnblogs.com/mellowsmile/p/4616612.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!