Simple Example of Sending Attachments Using UTL_SMTP

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

Return to Blog