Search code examples
javaspringjakarta-mail

Switched to JakartaMail org.springframework.mail.javamail.JavaMailSender InputStreamResource from byte array


I'm moving from mailx to Jakarta.

https://docs.openrewrite.org/recipes/java/migrate/jakarta/javaxmailtojakartamail

or with IntelliJ

import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
...snip


        try {
           MimeMessagePreparator messagePreparator = mimeMessage -> {
                MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage,true);
                messageHelper.setFrom("[email protected]");
                messageHelper.setTo(InternetAddress.parse("[email protected]"));
                messageHelper.setSubject("error log");
                messageHelper.setText(" Please find attachment:");

                //attachment
                byte[] data;
                if (emailDTO.getIsForCheckIn()) {
                    data = pdfService.generatePDFForCheckIn(emailDTO);
                } else if (emailDTO.getIsForPaymentRefund()) {
                    data = pdfService.generatePDFForRefund(emailDTO);
                } else {
                    data = pdfService.generatePDFForOnlineBooking(emailDTO);
                }

                String fileName = String.format("Booking-%s.pdf",emailDTO.getOnlineRentSell().get_id()).replace(":","");
                ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
                messageHelper.addAttachment(fileName, new InputStreamResource(byteArrayInputStream) );
            };

            // Sending the mail
            javaMailSender.send(messagePreparator);
        }

        // Catch block to handle MessagingException
        catch (Exception ex) {
            log.error(ex);
        }

This results in an error:

Passed-in Resource contains an open stream: invalid argument. JavaMail requires an InputStreamSource that creates a fresh stream for every call.

It works with a local file

FileSystemResource file = new FileSystemResource("/Users/work2/Documents/GitHub/srr-all6/srr-online-shop-backend/src/main/resources/error.jpeg");
messageHelper.addAttachment(file.getFilename(), file);

But not from byte

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
// ByteArrayResource byteArrayResource = new ByteArrayResource(data);
String fileName = String.format("Booking-%s.pdf",emailDTO.getOnlineRentSell().get_id()).replace(":","");
// ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
messageHelper.addAttachment(fileName, new InputStreamResource(byteArrayInputStream) );

Working but bad TMP file:

File tempFile = File.createTempFile(prefix, ".pdf", null);
tmpFilename.set(tempFile.getAbsolutePath());
FileOutputStream fos = new FileOutputStream(tempFile);
fos.write(byteArray);

FileSystemResource file = new FileSystemResource(tmpFilename.get());

messageHelper.addAttachment(fileName, file);

Could someone please point me to a working solution.


Solution

  • You need to use a ByteArrayResource instead of wrapping a byte array in a ByteArrayInputStream and wrapping that in an InputStreamResource.

    The documentation of ByteArrayResource even helpfully states (emphasis mine):

    Useful for loading content from any given byte array, without having to resort to a single-use InputStreamResource. Particularly useful for creating mail attachments from local content, where JavaMail needs to be able to read the stream multiple times.

    (JavaMail is the old name for JakartaMail.)