Exemple simple d'envoi de pièces jointes avec UTL_SMTP
S'APPLIQUE À : PL/SQL - Version 10.1.0.2 et suivantes
PL/SQL - Version 10.1.0.2 et suivantes
Les informations de ce document s'appliquent à toutes les plateformes.
*** Pertinence vérifiée le 25 juillet 2017 ***
OBJECTIF : packages PL/SQL UTL_SMTP, DBMS_LOB et UTL_ENCODE
Comment envoyer un courriel avec pièce jointe au moyen du package PL/SQL UTL_SMTP. Le code d'exemple utilise le package DBMS_LOB pour ouvrir et lire le fichier indiqué, et encode la pièce jointe au format base64 avec le package UTL_ENCODE. Cette méthode fonctionne avec la plupart des types de fichiers, mais vous devrez modifier le type MIME comme l'indiquent les commentaires du code.
À partir de la version 11.1.0.6, les ACL doivent être configurées avant d'exécuter le code d'exemple. Si les ACL ne sont pas configurées, le code échoue avec l'erreur ORA-24247. Consultez la note 1209644.1 pour savoir comment configurer les ACL.
SOLUTION :
1) Créez la procédure ci-dessous dans le schéma à partir duquel vous devez envoyer le courriel avec pièce jointe.
REMARQUE :
- Ce code suppose qu'un objet répertoire (directory) PL/SQL valide existe et qu'il pointe vers un emplacement valide de votre système de fichiers.
- Avec ce code d'exemple, une seule pièce jointe peut être envoyée par courriel. Pour envoyer plusieurs pièces jointes, consultez la 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. Appelez la procédure en remplaçant les valeurs entre chevrons par les valeurs appropriées.
REMARQUE :
- L'objet répertoire (directory) PL/SQL doit exister et pointer vers un emplacement valide de votre système de fichiers.
- Le nom de fichier que vous passez doit exister dans le système de fichiers.
Sinon, le code échouera avec une erreur.
set serveroutput on
exec mail_files('<from_email_id>', '<to_email_id>', '<subject_line>', 'message_body', '<directory_name>', '<file_name_to_be_attached>');