【问题标题】:Spring Mail - sending email in separate thread can't find the attachments temp filesSpring Mail - 在单独的线程中发送电子邮件找不到附件临时文件
【发布时间】:2019-09-26 11:27:57
【问题描述】:

当尝试在新线程中发送带有附件的电子邮件时,我得到 java.io.FileNotFoundException

该功能是 Java 8、带有嵌入式 Tomcat 的 Spring boot 2.1.5 应用程序的一部分。 我从前端表单中获取了一些输入数据,有时还带有附件。在业务逻辑中的某个时刻,会发送一封电子邮件,其中包含表单数据和附件(如果有)。

我想以非阻塞方式发送电子邮件,所以我在新线程中运行准备和发送电子邮件方法,但 JavaMailSender.send() 方法找不到附件。 通过 REST Api 从前端表单接收到的文件被 Tomcat 保存为临时文件夹中的临时文件,并且可能在发送电子邮件之前被删除。

电子邮件本身是带有 Multipart 的 MimeMessage,包含电子邮件文本和一到三个附件,大小不超过 10 MB。

在新线程中发送 mial 的主要逻辑:

public FormConfirmation productReturn(FormDataInput formData, List<MultipartFile> attachments) {

        try {
            FormConfirmation formConfirmation = formSendingService.prepareAndSendFormData(formData);
            return complaintConfirmation;
        } catch (Exception e) {
            new Thread(() -> notificationSendingService.notifyAboutUnhandledForm(formData, attachments)).start();
            throw new ErrorSendingFormException(e);
        }

    }

根本原因异常:

Caused by: org.springframework.mail.MailSendException: Failed messages: javax.mail.MessagingException: IOException while sending message;
  nested exception is:
    java.io.FileNotFoundException: C:\Users\pawelk\AppData\Local\Temp\tomcat.2114838244298062234.8080\work\Tomcat\localhost\ROOT\upload_1e03fce2_e99b_459c_87b0_b79bbe9eb7ca_00000001.tmp (The system cannot find the file specified);

邮件发送方式

public void notifyAboutUnhandledForm(FormDataInput formData, List<MultipartFile> attachments) {
        try {
            MimeMessage message = emailPreparer.prepareNotificationEmail(formData, attachments);
            emailSender.send(message);
        } catch (MessagingException | MailException | IOException ex) {
            logger.error(EMAIL_SENDING_EXCEPTION_MESSAGE + ex);
            throw new ErrorSendingFormException(EMAIL_SENDING_EXCEPTION_MESSAGE, ex);
        }
    }

完整的电子邮件准备课程:

@Service
public class EmailMessagePreparer {

    public static final String EMAIL_TITLE = "Email title";
    @Value("${spring.mail.username}")
    private String senderAddress;
    @Value("#{'${email.notification.recipients}'.split(',')}")
    private String[] emailRecipients;
    @Inject
    private EmailTemplateProvider templateProvider;
    @Inject
    private JavaMailSender emailSender;

    MimeMessage prepareNotificationEmail(FormDataInput formData, List<MultipartFile> attachments) throws MessagingException, IOException {
        MimeMessage message = emailSender.createMimeMessage();

        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setTo(emailRecipients);
        helper.setSubject(EMAIL_TITLE);
        helper.setFrom(senderAddress);
        helper.setText(templateProvider.provideEmailContentFromHTMLTemplate(formData), true);

        for (MultipartFile multipart : attachments) {
            addAttachmentToTheEmailMessage(helper, multipart);
        }
        return message;
    }

    private void addAttachmentToTheEmailMessage(MimeMessageHelper helper, MultipartFile multipart) throws IOException, MessagingException {
        helper.addAttachment(multipart.getOriginalFilename(), multipart);
    }

    private String extractAndWriteFileExtension(MultipartFile multipart) {
        return multipart.getOriginalFilename().substring(multipart.getOriginalFilename().lastIndexOf("."));
    }

}

当我在同一个主线程中发送电子邮件时,临时文件始终可用并且执行正确。 我希望在单独的线程中运行邮件发送方法应该对临时文件具有相同的访问权限,这些临时文件在程序使用之前不会被删除。

当发送不带附件或在单个主线程中带附件的电子邮件时,一切正常。

任何人都知道什么会导致问题以及如何解决它?

【问题讨论】:

  • 你的思考过程可能是对的。我会将临时附件文件复制到另一个目录,启动第二个发送邮件的进程,并在邮件成功发送后删除临时附件文件。
  • 我尝试从这些 MultipartFiles 创建新的单独临时文件,并将它们添加到 EmallMessagePreparer 类的电子邮件中(已经在新线程中运行),但这并没有解决问题。即使我没有通过 File.delete(); 手动删除它们;在 JavaMailSender 发送电子邮件之前,它们从 Tomcat 临时文件夹中消失了。当只有一个主线程时,此版本中的代码仍然有效。

标签: java spring spring-boot tomcat jakarta-mail


【解决方案1】:

不确定是否仍然需要,但我找到了解决方案:

问题是 multipartFile 的流被初始线程关闭,因此在异步发送电子邮件的那一刻,流已经关闭。一个快速的解决方案是将 multipartfile 转换为字节数组 (multipartfile.getBytes()),并将其传递给您的异步调用而不是传递 MultipartFile,然后您可以简单地将其附加到您的助手:

helper.addAttachment(name, new ByteArrayResource(byte_array))

【讨论】:

    猜你喜欢
    • 2015-07-08
    • 1970-01-01
    • 2011-09-30
    • 2014-10-03
    • 1970-01-01
    • 2019-06-19
    • 2019-09-01
    • 2015-07-08
    • 2013-01-06
    相关资源
    最近更新 更多