Oracle邮件推送函数

  1 CREATE OR REPLACE PROCEDURE PROCSENDEMAIL (
  2     P_TXT VARCHAR2,
  3     P_SUB VARCHAR2,
  4     P_SENDOR VARCHAR2,
  5     P_RECEIVER VARCHAR2,
  6     P_SERVER VARCHAR2,
  7     P_PORT NUMBER DEFAULT 25 ,
  8     P_NEED_SMTP INT DEFAULT 0 ,
  9     P_USER VARCHAR2 DEFAULT NULL ,
 10     P_PASS VARCHAR2 DEFAULT NULL ,
 11     P_FILENAME VARCHAR2 DEFAULT NULL ,
 12     P_ENCODE VARCHAR2 DEFAULT 'bit 7'
 13     )
 14     AUTHID CURRENT_USER
 15     IS
 16     /*
 17     作用:用oracle发送邮件
 18     主要功能:1、支持多收件人。
 19     2、支持中文
 20     3、支持抄送人
 21     4、支持大于32K的附件
 22     5、支持多行正文
 23     6、支持多附件
 24     7、支持文本附件和二进制附件
 25     8、支持HTML格式
 26     8、支持
 27     作者:suk
 28     参数说明:
 29     p_txt :邮件正文
 30     p_sub: 邮件标题
 31     p_SendorAddress : 发送人邮件地址
 32     p_ReceiverAddress : 接收地址,可以同时发送到多个地址上,地址之间用","或者";"隔开
 33     p_EmailServer : 邮件服务器地址,可以是域名或者IP
 34     p_Port :邮件服务器端口
 35     p_need_smtp:是否需要smtp认证,0表示不需要,1表示需要
 36     p_user:smtp验证需要的用户名
 37     p_pass:smtp验证需要的密码
 38     p_filename:附件名称,必须包含完整的路径,如"d:\temp\a.txt"。
 39     可以有多个附件,附件名称只见用逗号或者分号分隔
 40     p_encode:附件编码转换格式,其中 p_encode='bit 7' 表示文本类型附件
 41     p_encode='base64' 表示二进制类型附件
 42     注意:
 43     1、对于文本类型的附件,不能用base64的方式发送,否则出错
 44     2、对于多个附件只能用同一种格式发送
 45     测试
 46     PROCSENDEMAIL('中文测试邮件','中文主题','xiaoxiaolin_jack@163.com','258202898@qq.com,jack@adm-soft.com','smtp.163.com',25,1,'xiaoxiaolin_jack@163.com','xxl121','d:\test.txt','bit 7');
 47     PROCSENDEMAIL('中文测试邮件','中文主题','xiaoxiaolin_jack@163.com','258202898@qq.com,jack@adm-soft.com','smtp.163.com',25,1,'xiaoxiaolin_jack@163.com','xxl121','d:\pic.jpg,d:\test.txt','base64');
 48     PROCSENDEMAIL('中文测试邮件','中文主题','xiaoxiaolin_jack@163.com','258202898@qq.com,jack@adm-soft.com','smtp.163.com',25,1,'xiaoxiaolin_jack@163.com','xxl121','d:\unload.txt,d:\pic.jpg,d:\unload.xls','base64');
 49     --使用‘bit 7’能够正常执行
 50     */
 51     L_CRLF VARCHAR2 (2) := UTL_TCP.CRLF;
 52     L_SENDORADDRESS VARCHAR2 (4000);
 53     L_SPLITE VARCHAR2 (10) := '++';
 54     BOUNDARY CONSTANT VARCHAR2 (256) := '-----BYSUK';
 55     FIRST_BOUNDARY CONSTANT VARCHAR2 (256) := '--' || BOUNDARY || L_CRLF;
 56     LAST_BOUNDARY CONSTANT VARCHAR2 (256)
 57     := '--' || BOUNDARY || '--' || L_CRLF ;
 58     MULTIPART_MIME_TYPE CONSTANT VARCHAR2 (256)
 59     := 'multipart/mixed; boundary="' || BOUNDARY || '"' ;
 60     /* 以下部分是发送大二进制附件时用到的变量 */
 61     L_FIL BFILE;
 62     L_FILE_LEN NUMBER;
 63     L_MODULO NUMBER;
 64     L_PIECES NUMBER;
 65     L_FILE_HANDLE UTL_FILE.FILE_TYPE;
 66     L_AMT BINARY_INTEGER := 672 * 3; /* ensures proper format; 2016 */
 67     L_FILEPOS PLS_INTEGER := 1; /* pointer for the file */
 68     L_CHUNKS NUMBER;
 69     L_BUF RAW (2100);
 70     L_DATA RAW (2100);
 71     L_MAX_LINE_WIDTH NUMBER := 54;
 72     L_DIRECTORY_BASE_NAME VARCHAR2 (100) := 'DIR_FOR_SEND_MAIL';
 73     L_LINE VARCHAR2 (1000);
 74     L_MESG VARCHAR2 (32767);
 75     /* 以上部分是发送大二进制附件时用到的变量 */
 76     TYPE ADDRESS_LIST
 77     IS
 78     TABLE OF VARCHAR2 (100)
 79     INDEX BY BINARY_INTEGER;
 80     MY_ADDRESS_LIST ADDRESS_LIST;
 81     TYPE ACCT_LIST
 82     IS
 83     TABLE OF VARCHAR2 (100)
 84     INDEX BY BINARY_INTEGER;
 85     MY_ACCT_LIST ACCT_LIST;
 86      -------------------------------------返回附件源文件所在目录或者名称-----------------------
 87     FUNCTION GET_FILE (P_FILE VARCHAR2, P_GET INT)
 88     RETURN VARCHAR2
 89     IS
 90     --p_get=1 表示返回目录
 91     --p_get=2 表示返回文件名
 92     L_FILE VARCHAR2 (1000);
 93     BEGIN
 94     IF INSTR (P_FILE, '\') > 0
 95     THEN
 96     --windows
 97     IF P_GET = 1
 98     THEN
 99     L_FILE := SUBSTR (P_FILE, 1, INSTR (P_FILE, '\', -1) - 1);
100     ELSIF P_GET = 2
101     THEN
102     L_FILE :=
103     SUBSTR (P_FILE, - (LENGTH (P_FILE) - INSTR (P_FILE, '\', -1)));
104     END IF;
105     ELSIF INSTR (P_FILE, '/') > 0
106     THEN
107     --linux/unix
108     IF P_GET = 1
109     THEN
110     L_FILE := SUBSTR (P_FILE, 1, INSTR (P_FILE, '/', -1) - 1);
111     ELSIF P_GET = 2
112     THEN
113     L_FILE :=
114     SUBSTR (P_FILE, - (LENGTH (P_FILE) - INSTR (P_FILE, '/', -1)));
115     END IF;
116     END IF;
117     RETURN L_FILE;
118     END;
119     ---------------------------------------------删除directory------------------------------------
120     PROCEDURE DROP_DIRECTORY (P_DIRECTORY_NAME VARCHAR2)
121     IS
122     BEGIN
123     EXECUTE IMMEDIATE 'drop directory ' || P_DIRECTORY_NAME;
124     EXCEPTION
125     WHEN OTHERS
126     THEN
127     NULL;
128     END;
129     --------------------------------------------------创建directory-----------------------------------------
130     PROCEDURE CREATE_DIRECTORY (P_DIRECTORY_NAME VARCHAR2, P_DIR VARCHAR2)
131     IS
132     BEGIN
133     EXECUTE IMMEDIATE 'create directory '
134     || P_DIRECTORY_NAME
135     || ' as '''
136     || P_DIR
137     || '''';
138     EXECUTE IMMEDIATE 'grant read,write on directory '
139     || P_DIRECTORY_NAME
140     || ' to public';
141     EXCEPTION
142     WHEN OTHERS
143     THEN
144     RAISE;
145     END;
146     --------------------------------------------分割邮件地址或者附件地址--------------------
147     PROCEDURE P_SPLITE_STR (P_STR VARCHAR2, P_SPLITE_FLAG INT DEFAULT 1 )
148     IS
149     L_ADDR VARCHAR2 (254) := '';
150     L_LEN INT;
151     L_STR VARCHAR2 (4000);
152     J INT := 0; --表示邮件地址或者附件的个数
153     BEGIN
154     /*处理接收邮件地址列表,包括去空格、将;转换为,等*/
155     L_STR :=
156     TRIM (RTRIM (REPLACE (REPLACE (P_STR, ';', ','), ' ', ''), ','));
157     L_LEN := LENGTH (L_STR);
158     FOR I IN 1 .. L_LEN
159     LOOP
160     IF SUBSTR (L_STR, I, 1) <> ','
161     THEN
162     L_ADDR := L_ADDR || SUBSTR (L_STR, I, 1);
163     ELSE
164     J := J + 1;
165     IF P_SPLITE_FLAG = 1
166     THEN --表示处理邮件地址
167     --前后需要加上'<>',否则很多邮箱将不能发送邮件
168     L_ADDR := '<' || L_ADDR || '>';
169     --调用邮件发送过程
170     MY_ADDRESS_LIST (J) := L_ADDR;
171     ELSIF P_SPLITE_FLAG = 2
172     THEN --表示处理附件名称
173     MY_ACCT_LIST (J) := L_ADDR;
174     END IF;
175     L_ADDR := '';
176     END IF;
177     IF I = L_LEN
178     THEN
179     J := J + 1;
180     IF P_SPLITE_FLAG = 1
181     THEN
182     --调用邮件发送过程
183     L_ADDR := '<' || L_ADDR || '>';
184     MY_ADDRESS_LIST (J) := L_ADDR;
185     ELSIF P_SPLITE_FLAG = 2
186     THEN
187     MY_ACCT_LIST (J) := L_ADDR;
188     END IF;
189     END IF;
190     END LOOP;
191     END;
192     ------------------------------------------------写邮件头和邮件内容------------------------------------------
193     PROCEDURE WRITE_DATA (P_CONN IN OUT NOCOPY UTL_SMTP.CONNECTION,
194     P_NAME IN VARCHAR2,
195     P_VALUE IN VARCHAR2,
196     P_SPLITE VARCHAR2 DEFAULT ':' ,
197     P_CRLF VARCHAR2 DEFAULT L_CRLF )
198     IS
199     BEGIN
200     /* utl_raw.cast_to_raw 对解决中文乱码问题很重要*/
201     UTL_SMTP.WRITE_RAW_DATA (
202     P_CONN,
203     UTL_RAW.CAST_TO_RAW (
204     CONVERT (P_NAME || P_SPLITE || P_VALUE || P_CRLF, 'ZHS16GBK')
205     )
206     );
207     END;
208     ----------------------------------------写MIME邮件尾部-----------------------------------------------------
209     PROCEDURE END_BOUNDARY (CONN IN OUT NOCOPY UTL_SMTP.CONNECTION,
210     LAST IN BOOLEAN DEFAULT FALSE )
211     IS
212     BEGIN
213     UTL_SMTP.WRITE_DATA (CONN, UTL_TCP.CRLF);
214     IF (LAST)
215     THEN
216     UTL_SMTP.WRITE_DATA (CONN, LAST_BOUNDARY);
217     END IF;
218     END;
219     ----------------------------------------------发送附件----------------------------------------------------
220     PROCEDURE ATTACHMENT (
221     CONN IN OUT NOCOPY UTL_SMTP.CONNECTION,
222     MIME_TYPE IN VARCHAR2 DEFAULT 'text/plain' ,
223     INLINE IN BOOLEAN DEFAULT TRUE ,
224     FILENAME IN VARCHAR2 DEFAULT 't.txt' ,
225     TRANSFER_ENC IN VARCHAR2 DEFAULT '7 bit' ,
226     DT_NAME IN VARCHAR2 DEFAULT '0'
227     )
228     IS
229     L_FILENAME VARCHAR2 (1000);
230     BEGIN
231     --写附件头
232     UTL_SMTP.WRITE_DATA (CONN, FIRST_BOUNDARY);
233     --设置附件格式
234     WRITE_DATA (CONN, 'Content-Type', MIME_TYPE);
235     --如果文件名称非空,表示有附件
236     DROP_DIRECTORY (DT_NAME);
237     --创建directory
238     CREATE_DIRECTORY (DT_NAME, GET_FILE (FILENAME, 1));
239     --得到附件文件名称
240     L_FILENAME := GET_FILE (FILENAME, 2);
241     IF (INLINE)
242     THEN
243     WRITE_DATA (CONN,
244     'Content-Disposition',
245     'inline; filename="' || L_FILENAME || '"');
246     ELSE
247     WRITE_DATA (CONN,
248     'Content-Disposition',
249     'attachment; filename="' || L_FILENAME || '"');
250     END IF;
251     --设置附件的转换格式
252     IF (TRANSFER_ENC IS NOT NULL)
253     THEN
254     WRITE_DATA (CONN, 'Content-Transfer-Encoding', TRANSFER_ENC);
255     END IF;
256     UTL_SMTP.WRITE_DATA (CONN, UTL_TCP.CRLF);
257     --begin 贴附件内容
258     IF TRANSFER_ENC = 'bit 7'
259     THEN
260     --如果是文本类型的附件
261     BEGIN
262     L_FILE_HANDLE := UTL_FILE.FOPEN (DT_NAME, L_FILENAME, 'r'); --打开文件
263     --把附件分成多份,这样可以发送超过32K的附件
264     LOOP
265     UTL_FILE.GET_LINE (L_FILE_HANDLE, L_LINE);
266     L_MESG := L_LINE || L_CRLF;
267     WRITE_DATA (CONN,
268     '',
269     L_MESG,
270     '',
271     '');
272     END LOOP;
273     UTL_FILE.FCLOSE (L_FILE_HANDLE);
274     END_BOUNDARY (CONN);
275     EXCEPTION
276     WHEN OTHERS
277     THEN
278     UTL_FILE.FCLOSE (L_FILE_HANDLE);
279     END_BOUNDARY (CONN);
280     NULL;
281     END; --结束文本类型附件的处理
282     ELSIF TRANSFER_ENC = 'base64'
283     THEN
284     --如果是二进制类型的附件
285     BEGIN
286     --把附件分成多份,这样可以发送超过32K的附件
287     L_FILEPOS := 1; --重置offset,在发送多个附件时,必须重置
288     L_FIL := BFILENAME (DT_NAME, L_FILENAME);
289     L_FILE_LEN := DBMS_LOB.GETLENGTH (L_FIL);
290     L_MODULO := MOD (L_FILE_LEN, L_AMT);
291     L_PIECES := TRUNC (L_FILE_LEN / L_AMT);
292     IF (L_MODULO <> 0)
293     THEN
294     L_PIECES := L_PIECES + 1;
295     END IF;
296     DBMS_LOB.FILEOPEN (L_FIL, DBMS_LOB.FILE_READONLY);
297     DBMS_LOB.READ (L_FIL,
298     L_AMT,
299     L_FILEPOS,
300     L_BUF);
301     L_DATA := NULL;
302     FOR I IN 1 .. L_PIECES
303     LOOP
304     L_FILEPOS := I * L_AMT + 1;
305     L_FILE_LEN := L_FILE_LEN - L_AMT;
306     L_DATA := UTL_RAW.CONCAT (L_DATA, L_BUF);
307     L_CHUNKS := TRUNC (UTL_RAW.LENGTH (L_DATA) / L_MAX_LINE_WIDTH);
308     IF (I <> L_PIECES)
309     THEN
310     L_CHUNKS := L_CHUNKS - 1;
311     END IF;
312     UTL_SMTP.WRITE_RAW_DATA (CONN,
313     UTL_ENCODE.BASE64_ENCODE (L_DATA));
314     L_DATA := NULL;
315     IF (L_FILE_LEN < L_AMT AND L_FILE_LEN > 0)
316     THEN
317     L_AMT := L_FILE_LEN;
318     END IF;
319     DBMS_LOB.READ (L_FIL,
320     L_AMT,
321     L_FILEPOS,
322     L_BUF);
323     END LOOP;
324     DBMS_LOB.FILECLOSE (L_FIL);
325     END_BOUNDARY (CONN);
326     EXCEPTION
327     WHEN OTHERS
328     THEN
329     DBMS_LOB.FILECLOSE (L_FIL);
330     END_BOUNDARY (CONN);
331     RAISE;
332     END; --结束处理二进制附件
333     END IF; --结束处理附件内容
334     DROP_DIRECTORY (DT_NAME);
335     END; --结束过程ATTACHMENT
336     ---------------------------------------------真正发送邮件的过程--------------------------------------------
337     PROCEDURE P_EMAIL (P_SENDORADDRESS2 VARCHAR2, --发送地址
338     P_RECEIVERADDRESS2 VARCHAR2) --接受地址
339     IS
340     L_CONN UTL_SMTP.CONNECTION; --定义连接
341     BEGIN
342     /*初始化邮件服务器信息,连接邮件服务器*/
343     L_CONN := UTL_SMTP.OPEN_CONNECTION (P_SERVER, P_PORT);
344     UTL_SMTP.HELO (L_CONN, P_SERVER);
345     /* smtp服务器登录校验 */
346     IF P_NEED_SMTP = 1
347     THEN
348     UTL_SMTP.COMMAND (L_CONN, 'AUTH LOGIN', '');
349     UTL_SMTP.COMMAND (
350     L_CONN,
351     UTL_RAW.CAST_TO_VARCHAR2 (
352     UTL_ENCODE.BASE64_ENCODE (UTL_RAW.CAST_TO_RAW (P_USER))
353     )
354     );
355     UTL_SMTP.COMMAND (
356     L_CONN,
357     UTL_RAW.CAST_TO_VARCHAR2 (
358     UTL_ENCODE.BASE64_ENCODE (UTL_RAW.CAST_TO_RAW (P_PASS))
359     )
360     );
361     END IF;
362     /*设置发送地址和接收地址*/
363     UTL_SMTP.MAIL (L_CONN, P_SENDORADDRESS2);
364     UTL_SMTP.RCPT (L_CONN, P_RECEIVERADDRESS2);
365     /*设置邮件头*/
366     UTL_SMTP.OPEN_DATA (L_CONN);
367     WRITE_DATA (L_CONN, 'Date', TO_CHAR (SYSDATE, 'yyyy-mm-dd hh24:mi:ss'));
368     /*设置发送人*/
369     WRITE_DATA (L_CONN, 'From', P_SENDOR);
370     /*设置接收人*/
371     WRITE_DATA (L_CONN, 'To', P_RECEIVER);
372     /*设置邮件主题*/
373     WRITE_DATA (L_CONN, 'Subject', P_SUB);
374     WRITE_DATA (L_CONN, 'Content-Type', MULTIPART_MIME_TYPE);
375     UTL_SMTP.WRITE_DATA (L_CONN, UTL_TCP.CRLF);
376     UTL_SMTP.WRITE_DATA (L_CONN, FIRST_BOUNDARY);
377     WRITE_DATA (L_CONN, 'Content-Type', 'text/plain;charset=gb2312');
378     --单独空一行,否则,正文内容不显示
379     UTL_SMTP.WRITE_DATA (L_CONN, UTL_TCP.CRLF);
380     /* 设置邮件正文
381     把分隔符还原成chr(10)。这主要是为了shell中调用该过程,如果有多行,则先把多行的内容合并成一行,并用 l_splite分隔
382     然后用 l_crlf替换chr(10)。这一步是必须的,否则将不能发送邮件正文有多行的邮件
383     */
384     WRITE_DATA (
385     L_CONN,
386     '',
387     REPLACE (REPLACE (P_TXT, L_SPLITE, CHR (10)), CHR (10), L_CRLF),
388     '',
389     ''
390     );
391     END_BOUNDARY (L_CONN);
392     --如果文件名称不为空,则发送附件
393     IF (P_FILENAME IS NOT NULL)
394     THEN
395     --根据逗号或者分号拆分附件地址
396     P_SPLITE_STR (P_FILENAME, 2);
397     --循环发送附件(在同一个邮件中)
398     FOR K IN 1 .. MY_ACCT_LIST.COUNT
399     LOOP
400     ATTACHMENT (
401     CONN => L_CONN,
402     FILENAME => MY_ACCT_LIST (K),
403     TRANSFER_ENC => P_ENCODE,
404     DT_NAME => L_DIRECTORY_BASE_NAME || TO_CHAR (K)
405     );
406     END LOOP;
407     END IF;
408     /*关闭数据写入*/
409     UTL_SMTP.CLOSE_DATA (L_CONN);
410     /*关闭连接*/
411     UTL_SMTP.QUIT (L_CONN);
412     /*异常处理*/
413     EXCEPTION
414     WHEN OTHERS
415     THEN
416     NULL;
417     RAISE;
418     END;
419     ---------------------------------------------------主过程-----------------------------------------------------
420     BEGIN
421     L_SENDORADDRESS := '<' || P_SENDOR || '>';
422     P_SPLITE_STR (P_RECEIVER); --处理邮件地址
423     FOR K IN 1 .. MY_ADDRESS_LIST.COUNT
424     LOOP
425     P_EMAIL (L_SENDORADDRESS, MY_ADDRESS_LIST (K));
426     END LOOP;
427     /*处理邮件地址,根据逗号分割邮件*/
428     EXCEPTION
429     WHEN OTHERS
430     THEN
431     RAISE;
432     --RAISE_APPLICATION_ERROR(-20000,'PROCSENDEMAIL异常中止');
433     END;

 

posted @ 2013-07-23 14:31  a坦然  阅读(611)  评论(0编辑  收藏  举报