Simple Example of Sending Attachments Using UTL_SMTP
Simple Example of Sending Attachments Using UTL_SMTP
Titleimage
Posted by Patrick Hamou on 2017:09:22 14:00:36
APPLIES TO: PL/SQL - Version 10.1.0.2 and later
PL/SQL - Version 10.1.0.2 and later
Information in this document applies to any platform.
*** Checked for relevance on 25th Jul 2017 ***
GOAL: PL/SQL package UTL_SMTP, DBMS_LOB package, UTL_ENCODE package
How to send an E-Mail with attachment using the PL/SQL package UTL_SMTP. The sample code uses the DBMS_LOB package to open and read the given file and encodes the attachment using UTL_ENCODE package to base64 format. This method will work with most types of file, but you will need to modify the mime type as noted in the code comments.
From version 11.1.0.6 onward's ACL needs to be configured before running the sample code. If ACL is not configured, the code will fail with ORA-24247 error. Refer to note 1209644.1 on how to configure ACL.
SOLUTION:
1) Create the below procedure in your schema from where you need to send E-Mail with attachment.
NOTE:
- This code assumes that a valid PL/SQL directory objects exists and its pointing to a valid location in your file system.
- Using this sample code, only one attachment can be sent per E-Mail. If multiple attachment needs to be sent, then refer to note 357385.1.
CREATE OR REPLACE PROCEDURE
mail_files (p_from_mail VARCHAR2,
p_to_mail VARCHAR2,
p_subject VARCHAR2,
p_message VARCHAR2,
p_oracle_directory VARCHAR2,
p_binary_file VARCHAR2)
IS
/*********************************************************************************
Example procedure to send a mail with an in line attachment encoded in Base64.
This procedure uses the following nested functions:
binary_attachment - calls:
begin_attachment - calls:
write_boundary
write_mime_header
end attachment - calls;
write_boundary
*********************************************************************************/
/* Change the SMTP_SERVER_NAME and PORT number according to your environment */
v_smtp_server VARCHAR2(100) := '<SMTP_SERVER_NAME>';
v_smtp_server_port NUMBER := 25;
v_directory_name VARCHAR2(100);
v_file_name VARCHAR2(100);
v_mesg VARCHAR2(32767);
v_conn UTL_SMTP.CONNECTION;
PROCEDURE write_mime_header(p_conn in out nocopy utl_smtp.connection,
p_name in varchar2,
p_value in varchar2)
IS
BEGIN
UTL_SMTP.WRITE_RAW_DATA(
p_conn,
UTL_RAW.CAST_TO_RAW( p_name || ': ' || p_value || UTL_TCP.CRLF)
);
END write_mime_header;
PROCEDURE write_boundary(p_conn IN OUT NOCOPY UTL_SMTP.CONNECTION,
p_last IN BOOLEAN DEFAULT false)
IS
BEGIN
IF (p_last) THEN
UTL_SMTP.WRITE_DATA(p_conn, '--DMW.Boundary.605592468--'||UTL_TCP.CRLF);
ELSE
UTL_SMTP.WRITE_DATA(p_conn, '--DMW.Boundary.605592468'||UTL_TCP.CRLF);
END IF;
END write_boundary;
PROCEDURE end_attachment(p_conn IN OUT NOCOPY UTL_SMTP.CONNECTION,
p_last IN BOOLEAN DEFAULT TRUE)
IS
BEGIN
UTL_SMTP.WRITE_DATA(p_conn, UTL_TCP.CRLF);
IF (p_last) THEN
write_boundary(p_conn, p_last);
END IF;
END end_attachment;
PROCEDURE begin_attachment(p_conn IN OUT NOCOPY UTL_SMTP.CONNECTION,
p_mime_type IN VARCHAR2 DEFAULT 'text/plain',
p_inline IN BOOLEAN DEFAULT false,
p_filename IN VARCHAR2 DEFAULT null,
p_transfer_enc IN VARCHAR2 DEFAULT null)
IS
BEGIN
write_boundary(p_conn);
IF (p_transfer_enc IS NOT NULL) THEN
write_mime_header(p_conn, 'Content-Transfer-Encoding',p_transfer_enc);
END IF;
write_mime_header(p_conn, 'Content-Type', p_mime_type);
IF (p_filename IS NOT NULL) THEN
IF (p_inline) THEN
write_mime_header(p_conn,'Content-Disposition', 'inline; filename="' || p_filename || '"');
ELSE
write_mime_header(p_conn,'Content-Disposition', 'attachment; filename="' || p_filename || '"');
END IF;
END IF;
UTL_SMTP.WRITE_DATA(p_conn, UTL_TCP.CRLF);
END begin_attachment;
PROCEDURE binary_attachment(p_conn IN OUT UTL_SMTP.CONNECTION,
p_file_name IN VARCHAR2,
p_mime_type in VARCHAR2)
IS
c_max_line_width CONSTANT PLS_INTEGER DEFAULT 54;
v_amt BINARY_INTEGER := 672 * 3; /* ensures proper format; 2016 */
v_bfile BFILE;
v_file_length PLS_INTEGER;
v_buf RAW(2100);
v_modulo PLS_INTEGER;
v_pieces PLS_INTEGER;
v_file_pos pls_integer := 1;
BEGIN
begin_attachment(p_conn => p_conn,
p_mime_type => p_mime_type,
p_inline => TRUE,
p_filename => p_file_name,
p_transfer_enc => 'base64');
BEGIN
v_bfile := BFILENAME(p_oracle_directory, p_file_name);
-- Get the size of the file to be attached
v_file_length := DBMS_LOB.GETLENGTH(v_bfile);
-- Calculate the number of pieces the file will be split up into
v_pieces := TRUNC(v_file_length / v_amt);
-- Calculate the remainder after dividing the file into v_amt chunks
v_modulo := MOD(v_file_length, v_amt);
IF (v_modulo <> 0) THEN
-- Since the file does not divide equally
-- we need to go round the loop an extra time to write the last
-- few bytes - so add one to the loop counter.
v_pieces := v_pieces + 1;
END IF;
DBMS_LOB.FILEOPEN(v_bfile, DBMS_LOB.FILE_READONLY);
FOR i IN 1 .. v_pieces LOOP
-- we can read at the beginning of the loop as we have already calculated
-- how many iterations we will take and so do not need to check
-- end of file inside the loop.
v_buf := NULL;
DBMS_LOB.READ(v_bfile, v_amt, v_file_pos, v_buf);
v_file_pos := I * v_amt + 1;
UTL_SMTP.WRITE_RAW_DATA(p_conn, UTL_ENCODE.BASE64_ENCODE(v_buf));
END LOOP;
END;
DBMS_LOB.FILECLOSE(v_bfile);
end_attachment(p_conn => p_conn);
EXCEPTION
WHEN NO_DATA_FOUND THEN
end_attachment(p_conn => p_conn);
DBMS_LOB.FILECLOSE(v_bfile);
END binary_attachment;
/*** MAIN Routine ***/
BEGIN
v_conn:= UTL_SMTP.OPEN_CONNECTION( v_smtp_server, v_smtp_server_port );
UTL_SMTP.HELO( v_conn, v_smtp_server );
UTL_SMTP.MAIL( v_conn, p_from_mail );
UTL_SMTP.RCPT( v_conn, p_to_mail );
UTL_SMTP.OPEN_DATA ( v_conn );
UTL_SMTP.WRITE_DATA(v_conn, 'Subject: '||p_subject||UTL_TCP.CRLF);
v_mesg:= 'Content-Transfer-Encoding: 7bit' || UTL_TCP.CRLF ||
'Content-Type: multipart/mixed;boundary="DMW.Boundary.605592468"' || UTL_TCP.CRLF ||
'Mime-Version: 1.0' || UTL_TCP.CRLF ||
'--DMW.Boundary.605592468' || UTL_TCP.CRLF ||
'Content-Transfer-Encoding: binary'||UTL_TCP.CRLF||
'Content-Type: text/plain' ||UTL_TCP.CRLF ||
UTL_TCP.CRLF || p_message || UTL_TCP.CRLF ;
UTL_SMTP.write_data(v_conn, 'To: ' || p_to_mail || UTL_TCP.crlf);
UTL_SMTP.WRITE_RAW_DATA ( v_conn, UTL_RAW.CAST_TO_RAW(v_mesg) );
/*** Add attachment here ***/
binary_attachment(p_conn => v_conn,
p_file_name => p_binary_file,
-- Modify the mime type at the beginning of this line depending
-- on the type of file being loaded.
p_mime_type => 'text/plain; name="'||p_binary_file||'"'
);
/*** Send E-Mail ***/
UTL_SMTP.CLOSE_DATA( v_conn );
UTL_SMTP.QUIT( v_conn );
END;
/
2. Call the procedure by replacing the values in angular brackets appropriately.
NOTE:
- The PL/SQL directory object should exist and point to a valid location in your file system.
- The file name that you are passing should exist in the file system.
Otherwise the code will fail with error.
set serveroutput on
exec mail_files('<from_email_id>', '<to_email_id>', '<subject_line>', 'message_body', '<directory_name>', '<file_name_to_be_attached>');
Posted by Patrick Hamou on 2017:09:22 14:00:36