如何使用 InputStream 和 Spring 发送带附件的电子邮件?

Posted

技术标签:

【中文标题】如何使用 InputStream 和 Spring 发送带附件的电子邮件?【英文标题】:How to send email with attachment using InputStream and Spring? 【发布时间】:2011-08-06 08:33:27 【问题描述】:

情况是这样的:

首先,我们在内存中生成一个文件,我们可以得到一个InputStream对象。 其次,InputStream 对象必须作为电子邮件的附件发送。语言是Java,我们使用Spring来发送邮件。

我找到了很多信息,但我找不到如何使用InputStream 发送电子邮件附件。我尝试这样做:

InputStreamSource iss= new InputStreamResource(new FileInputStream("c:\\a.txt"));
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true, "UTF-8");
message.addAttachment("attachment", iss);

但我得到一个例外:

传入的资源包含一个开放流:无效参数。邮件 需要一个 InputStreamSource 为每个创建一个新的流 打电话。

【问题讨论】:

你做到了吗?我有同样的问题。这不太有意义,因为 InputStreamResource 是新的并且以前没有使用过,不知道为什么 Spring 会抛出该异常?! @Stef 你做到了吗?到目前为止,我使用 ByteArrayOutputStream 管理的内容比: message.addAttachment(fileName, new ByteArrayResource(byteArrayOutputStream.toByteArray()));但是由于某种原因,它在 ide 上作为开发配置文件运行时起作用,而不是在作为 java -jar target/projectName.jar 构建和运行时起作用。我得到一个 com.sun.mail.smtp.SMTPSenderFailedException: 550 Request failed;邮箱不可用 对不起埃德蒙,我不记得了。我猜我一定有,但我什至不记得这是在哪个项目中 :) InputStreamResource 实现总是在方法 isOpen() 上返回 true。这就是问题所在。 【参考方案1】:

看看spring参考章节24.3 Using the JavaMail MimeMessageHelper

示例来自那里,我认为它确实希望您想做:

JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");

MimeMessage message = sender.createMimeMessage();

// use the true flag to indicate you need a multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("test@host.com");

helper.setText("Check out this image!");

// let's attach the infamous windows Sample file (this time copied to c:/)
FileSystemResource resource = new FileSystemResource(new File("c:/Sample.jpg"));

helper.addAttachment("CoolImage.jpg", resource );

sender.send(message);

如果你想使用 Stream,那么你可以使用

ByteArrayResource resource = new ByteArrayResource(IOUtils.toByteArray(inputStream)));

而不是文件系统资源

【讨论】:

是的,我知道这一点。我想知道的是如何使用 InputStream 对象而不是文件发送附件。因为我在内存中生成了一个文件,我不想把它保存在磁盘上。【参考方案2】:

对于在内存中生成的文件,您可以使用ByteArrayResource。只需使用 Apache Commons IO 库中的 IOUtils 转换您的 InputStream 对象。

很简单:

helper.addAttachment("attachement",
new ByteArrayResource(IOUtils.toByteArray(inputStream)));

【讨论】:

谢谢。完美运行。 这也可以在没有库的情况下使用以下方法完成:helper.addAttachment(ATTACHMENT_FILE_NAME, new ByteArrayResource(attachmentContent.getBytes(StandardCharsets.UTF_8)), MediaType.TEXT_html.toString());跨度> 既然附件被转换为字节[],有没有办法可以附加一个大于2GB的文件?【参考方案3】:

您可以根据要求对 InputStreamSource 进行简单的实现并在其中传递新的 InputStream:

InputStreamSource iss = new InputStreamSource() 
    @Override
    public InputStream getInputStream() throws IOException 
        // provide fresh InputStream
        return new FileInputStream("c:\\a.txt");
    

MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true, "UTF-8");
message.addAttachment("attachment", iss);

【讨论】:

【参考方案4】:

//inlineFileObjectCreated -- 你可以为示例创建一个StringBuilder对象

ByteArrayDataSource source = new ByteArrayDataSource("file name", "contentType", inlineFileObjectCreated.getBytes() );

                JavaMailSender mailSender = (JavaMailSender) ServicesHome.getService("javaMailSender");
                MimeMessage mimeMessage = mailSender.createMimeMessage();
                MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
                mimeMessageHelper.setTo(toArray);           
                mimeMessageHelper.setSubject("");
                mimeMessageHelper.setText("");
                mimeMessageHelper.addAttachment("filename", source);
                mailSender.send(mimeMessageHelper.getMimeMessage());

//////////////////////////////////////// p>

import javax.activation.DataSource;

    public class ByteArrayDataSource implements DataSource 
        byte[] bytes;
        String contentType;
        String name;

        public ByteArrayDataSource( String name, String contentType, byte[] bytes ) 
          this.name = name;
          this.bytes = bytes;
          this.contentType = contentType;
        

        public String getContentType() 
          return contentType;
        

        public InputStream getInputStream() 
          return new ByteArrayInputStream(bytes);
        

        public String getName() 
          return name;
        

        public OutputStream getOutputStream() throws IOException 
          throw new FileNotFoundException();
        
      

【讨论】:

【参考方案5】:

工作示例是:

1) 附件是一个InputStreamSource 接口

public void send() throws IOException, MessagingException 
    final ByteArrayOutputStream stream = createInMemoryDocument("body");
    final InputStreamSource attachment = new ByteArrayResource(stream.toByteArray());
    final MimeMessage message = javaMailSender.createMimeMessage();
    final MimeMessageHelper helper = new MimeMessageHelper(message, true);
    helper.setSubject("subject");
    helper.setFrom("from@from.com");
    helper.setTo("to@to.com");
    helper.setReplyTo("replyTo@replyTo.com");
    helper.setText("stub", false);
    helper.addAttachment("document.txt", attachment);
    javaMailSender.send(message);

2) 附件是一个DataSource 接口

public void send() throws IOException, MessagingException 
        final ByteArrayOutputStream document = createInMemoryDocument("body");
        final InputStream inputStream = new ByteArrayInputStream(document.toByteArray());
        final DataSource attachment = new ByteArrayDataSource(inputStream, "application/octet-stream");
        final MimeMessage message = javaMailSender.createMimeMessage();
        final MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setSubject("subject");
        helper.setFrom("from@from.com");
        helper.setTo("to@to.com");
        helper.setReplyTo("replyTo@replyTo.com");
        helper.setText("stub", false);
        helper.addAttachment("document.txt", attachment);
        javaMailSender.send(message);
    

解释:

传入的资源包含一个开放流:无效参数。 JavaMail 需要一个 InputStreamSource 来创建一个新的流 每次通话。

如果开发人员在isOpen() 方法中使用返回trueInputStreamSource 实现,则可能会出现此消息。

方法MimeMessageHelper#addAttacment()中有一个特殊的检查:

public void addAttachment(String attachmentFilename, InputStreamSource inputStreamSource, String contentType) 
    //...
    if (inputStreamSource instanceof Resource && ((Resource) inputStreamSource).isOpen()) 
        throw new IllegalArgumentException(
        "Passed-in Resource contains an open stream: invalid argument. " +
        "JavaMail requires an InputStreamSource that creates a fresh stream for every call.");
    
    //...

InputStreamResource#isOpen() 总是返回 true,这使得无法将此实现用作附件:

public class InputStreamResource extends AbstractResource 
   //...
   @Override
   public boolean isOpen() 
      return true;
   
   //...

【讨论】:

以上是关于如何使用 InputStream 和 Spring 发送带附件的电子邮件?的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot RestController - PostMapping - 将请求正文作为 InputStream [重复]

如何正确读取 Flux<DataBuffer> 并将其转换为单个 inputStream

spring webflux Flux<DataBuffer> 转换为 InputStream

spring boot — InputStream

Spring获取资源文件+从File+从InputStream对象获取正文数据

如何使用 videoView 和 inputStream 播放视频