I componenti Indy (TIdSMTP, TIdMessage) restano la via più diretta per inviare email da un'applicazione Delphi senza dipendenze esterne. La parte meno intuitiva non è la connessione SMTP in sé, ma la costruzione del messaggio quando servono sia allegati tradizionali sia immagini mostrate direttamente nel corpo HTML: per queste ultime serve un Content-ID che leghi l'allegato al tag <img> che lo referenzia.
La classe di invio
Incapsulare l'invio in una classe dedicata evita di sparpagliare la configurazione SMTP (host, porta, credenziali) tra i vari form da cui si spediscono email.
unit uEmailSender;
interface
uses
System.SysUtils, IdSMTP, IdMessage, IdAttachmentFile, IdText, IdExplicitTLSClientServerBase;
type
TEmailSender = class
private
FSMTPHost: string;
FSMTPPort: Integer;
FUsername: string;
FPassword: string;
procedure AddAttachment(const FileName: string; AMessage: TIdMessage);
procedure AddEmbeddedImage(const FileName, ContentID: string; AMessage: TIdMessage);
public
constructor Create(const SMTPHost: string; SMTPPort: Integer;
const Username, Password: string);
procedure SendEmail(const Recipient, Subject, Body: string;
const AttachmentFile: string = ''; const ImageFile: string = '');
end;
implementation
constructor TEmailSender.Create(const SMTPHost: string; SMTPPort: Integer;
const Username, Password: string);
begin
FSMTPHost := SMTPHost;
FSMTPPort := SMTPPort;
FUsername := Username;
FPassword := Password;
end;
procedure TEmailSender.AddAttachment(const FileName: string; AMessage: TIdMessage);
begin
if FileExists(FileName) then
TIdAttachmentFile.Create(AMessage.MessageParts, FileName);
end;Immagini incorporate nel corpo HTML
Per un'immagine visibile direttamente nel testo (un logo in intestazione, ad esempio) l'allegato riceve un ContentID e il corpo HTML lo referenzia con src="cid:...".
procedure TEmailSender.AddEmbeddedImage(const FileName, ContentID: string; AMessage: TIdMessage);
var
Attachment: TIdAttachmentFile;
TextPart: TIdText;
begin
if not FileExists(FileName) then
Exit;
Attachment := TIdAttachmentFile.Create(AMessage.MessageParts, FileName);
Attachment.ContentID := ContentID;
TextPart := TIdText.Create(AMessage.MessageParts, nil);
TextPart.ContentType := 'text/html';
TextPart.CharSet := 'utf-8';
TextPart.Body.Text :=
'
';
end;
procedure TEmailSender.SendEmail(const Recipient, Subject, Body: string;
const AttachmentFile: string; const ImageFile: string);
var
SMTP: TIdSMTP;
Message: TIdMessage;
begin
SMTP := TIdSMTP.Create(nil);
Message := TIdMessage.Create(nil);
try
SMTP.Host := FSMTPHost;
SMTP.Port := FSMTPPort;
SMTP.Username := FUsername;
SMTP.Password := FPassword;
Message.From.Address := FUsername;
Message.Recipients.Add.Address := Recipient;
Message.Subject := Subject;
Message.Body.Text := Body;
if AttachmentFile <> '' then
AddAttachment(AttachmentFile, Message);
if ImageFile <> '' then
AddEmbeddedImage(ImageFile, 'logoAziendale', Message);
SMTP.Connect;
try
SMTP.Send(Message);
finally
SMTP.Disconnect;
end;
finally
SMTP.Free;
Message.Free;
end;
end;
end.Un paio di accorgimenti pratici
Le credenziali SMTP (host, utente, password) vanno sempre lette da configurazione — mai scritte nel codice — e idealmente protette come descritto altrove per i file INI. Vale anche la pena avvolgere SMTP.Connect e SMTP.Send in un blocco che intercetta EIdSocketError ed EIdSMTPException separatamente dalle altre eccezioni, così da distinguere un problema di rete da un rifiuto del server (autenticazione, mittente non valido) e mostrare all'utente un messaggio realmente utile.