sábado, 25 de abril de 2020

Java Mail con adjuntos y rutas relativas


Buenas, hoy vamos a ver como enviar un correo electrónico con Java Mail. Pero además veremos algo más importante, como añadir recursos (plantilla HTML, imagenes) a través de rutas relativas. En muchos ejemplos podrás ver como se añaden estos recursos pero siempre poniendo la ruta absoluta a dichos recursos. La clave para ello está en añadir los recursos como InputStream y no File.

El ejemplo lo dividiremos en 5 partes diferentes. Empecemos por la configuración del servidor, pensada para usar el servidor de SMTP de GMail

// Set properties
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.port", "587");
// Create session & authenticate with mail server
Session session = Session.getInstance(props, new javax.mail.Authenticator() {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(username, password);
    }
});

Luego creamos el mensaje con los datos de envío: destinatario, emisor, asunto, etc.

Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(fromEmail));
message.addRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail));
message.setSubject(messageSubject);

El siguiente paso es añadir el contenido del mensaje. Para ello por un lado crearemos el HTML del contenido en base a una plantilla concreta.

private void createTemplate(final MimeMultipart content, final String relFilePath,
      final String encoding, final String type) throws IOException, MessagingException {
    StringWriter writer = new StringWriter();
    IOUtils.copy(MailSenderUtil.class.getClassLoader().getResource(relFilePath).openStream(), writer,
            encoding);
    MimeBodyPart textPart = new MimeBodyPart();
    textPart.setText(writer.toString(), encoding, type);
    content.addBodyPart(textPart);
}

Y añadiremos una imagen como ejemplo de como podemos añadir adjuntos.

private void addImage(final MimeMultipart content, final String relFilePath, final String contentId,
        final String disposition) throws MessagingException, IOException {
    MimeBodyPart messageBodyPart = new MimeBodyPart();
    InputStream iStream = getClass().getClassLoader().getResource(relFilePath).openStream();
    DataSource fds = new InputStreamDataSource(iStream, contentId, "image/jpg");
    messageBodyPart.setDataHandler(new DataHandler(fds));
    messageBodyPart.setHeader("Content-ID", "<" + contentId + ">");
    messageBodyPart.setDisposition(disposition);
    messageBodyPart.setFileName(contentId);
    content.addBodyPart(messageBodyPart);
}

La imagen se encuentra dentro de nuestro proyecto y para ello indicamos su ruta relativa. Si simplemente obtuvieramos la ruta a través del método getFile() del objeto InputStream, a la hora de enviar el correo fallaría, porque indicaría que no es posible encontrarlo. En cambio, si le pasamos un InputStream no tendríamos problema para acceder al contenido del fichero. Pero para ello tendremos que crear una clase que extienda de javax.activation.DataSource para almacenar InputStream, puesto que por defecto no existe. Este sería su código.

public class InputStreamDataSource implements DataSource {
    byte[] bytes = null;
    private final String name;
    private final String contentType;

    public InputStreamDataSource(final InputStream inputStream, final String name, final String contentType)
            throws IOException {
        bytes = IOUtils.toByteArray(inputStream);
        this.name = name;
        this.contentType = contentType;
    }
    @Override
    public String getContentType() {
        return new MimetypesFileTypeMap().getContentType(contentType);
    }
    @Override
    public InputStream getInputStream() throws IOException {
        return new ByteArrayInputStream(bytes);
    }
    @Override
    public String getName() {
        return name;
    }
    @Override
    public OutputStream getOutputStream() throws IOException {
        throw new IOException("Read-only data");
    }
}

Por último solo nos quedaría realizar propiamente el envío del correo.

MimeMultipart content = new MimeMultipart("related");
createTemplate(content, "content/html/basic.html", "UTF-8", "html");
addImage(content, "content/images/header-wallpaper.jpg", "header-wallpaper", Part.INLINE);
message.setContent(content);
Transport.send(message);

Si quieres ver todo el código, lo tienes aquí.

No hay comentarios:

Publicar un comentario