【问题标题】:Download attachments using Java Mail使用 Java Mail 下载附件
【发布时间】:2020-12-03 19:18:22
【问题描述】:

现在我已经下载了所有消息,并将它们存储到

Message[] temp;

如何获取每封邮件的附件列表

List<File> attachments;

注意:请不要使用第三方库,只有 JavaMail。

【问题讨论】:

    标签: java attachment jakarta-mail


    【解决方案1】:

    没有异常处理,但这里是:

    List<File> attachments = new ArrayList<File>();
    for (Message message : temp) {
        Multipart multipart = (Multipart) message.getContent();
    
        for (int i = 0; i < multipart.getCount(); i++) {
            BodyPart bodyPart = multipart.getBodyPart(i);
            if(!Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition()) &&
                   StringUtils.isBlank(bodyPart.getFileName())) {
                continue; // dealing with attachments only
            } 
            InputStream is = bodyPart.getInputStream();
            // -- EDIT -- SECURITY ISSUE --
            // do not do this in production code -- a malicious email can easily contain this filename: "../etc/passwd", or any other path: They can overwrite _ANY_ file on the system that this code has write access to!
    //      File f = new File("/tmp/" + bodyPart.getFileName());
            FileOutputStream fos = new FileOutputStream(f);
            byte[] buf = new byte[4096];
            int bytesRead;
            while((bytesRead = is.read(buf))!=-1) {
                fos.write(buf, 0, bytesRead);
            }
            fos.close();
            attachments.add(f);
        }
    }
    

    【讨论】:

    • 但是等一下,我们不应该在保存文件之前检查 if (bodyPart.getDisposition() == Part.ATTACHMENT){},这样它就不会保存文件的正文电子邮件?
    • StringUtils.isBlank() 不会比使用 !StringUtils.isNotBlank 更自然吗?
    • 此答案不考虑嵌套的多部分附件(例如,Thunderbird 常用)。为了能够找到嵌套的多部分附件,请参阅@mefi 答案。
    • sn-p 是一个等待发生的安全漏洞。我已经编辑了 sn-p 以突出显示这一点。
    • 当然,谢谢。这个sn-p只是为了演示怎么做,自然不是生产代码
    【解决方案2】:

    问题很老了,但也许它会对某人有所帮助。我想扩展大卫拉比诺维茨的答案。

    if(!Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition()))
    

    不应像您期望的那样返回所有附件,因为您可能会收到混合部分没有定义处置的邮件。

       ----boundary_328630_1e15ac03-e817-4763-af99-d4b23cfdb600
    Content-Type: application/octet-stream;
        name="00000000009661222736_236225959_20130731-7.txt"
    Content-Transfer-Encoding: base64
    

    所以在这种情况下,您还可以检查文件名。像这样:

    if (!Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition()) && StringUtils.isBlank(part.getFileName())) {...}
    

    编辑

    使用上述条件的完整工作代码..因为每个部分都可以封装另一个部分并且附件应该嵌套,所以使用递归遍历所有部分

    public List<InputStream> getAttachments(Message message) throws Exception {
        Object content = message.getContent();
        if (content instanceof String)
            return null;        
    
        if (content instanceof Multipart) {
            Multipart multipart = (Multipart) content;
            List<InputStream> result = new ArrayList<InputStream>();
    
            for (int i = 0; i < multipart.getCount(); i++) {
                result.addAll(getAttachments(multipart.getBodyPart(i)));
            }
            return result;
    
        }
        return null;
    }
    
    private List<InputStream> getAttachments(BodyPart part) throws Exception {
        List<InputStream> result = new ArrayList<InputStream>();
        Object content = part.getContent();
        if (content instanceof InputStream || content instanceof String) {
            if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition()) || StringUtils.isNotBlank(part.getFileName())) {
                result.add(part.getInputStream());
                return result;
            } else {
                return new ArrayList<InputStream>();
            }
        }
    
        if (content instanceof Multipart) {
                Multipart multipart = (Multipart) content;
                for (int i = 0; i < multipart.getCount(); i++) {
                    BodyPart bodyPart = multipart.getBodyPart(i);
                    result.addAll(getAttachments(bodyPart));
                }
        }
        return result;
    }
    

    【讨论】:

    • 表达式检查文件名是否为空或null。对吗?
    • 章鱼:是的。检查 CharSequence 是否不为空 ("")、不为 null 且不只是空格。
    • 嘿,你能告诉如何将 List 转换为 List 吗?
    • kumunda:嗨,您应该迭代该列表并使用 org.apache.commons.io.FileUtils.copyInputStreamToFile(InputStream source, File destination) 并将文件添加到另一个集合。如果您使用 Guava,请检查 Lists.transform(...),您可以在迭代中使用它(取决于您需要如何初始化每个 File 实例)
    【解决方案3】:

    为保存附件文件的代码节省一些时间:

    用javax邮件版本1.4及以后,可以说

    // SECURITY LEAK - do not do this! Do not trust the 'getFileName' input. Imagine it is: "../etc/passwd", for example.
    // bodyPart.saveFile("/tmp/" + bodyPart.getFileName());
    

    而不是

        InputStream is = bodyPart.getInputStream();
        File f = new File("/tmp/" + bodyPart.getFileName());
        FileOutputStream fos = new FileOutputStream(f);
        byte[] buf = new byte[4096];
        int bytesRead;
        while((bytesRead = is.read(buf))!=-1) {
            fos.write(buf, 0, bytesRead);
        }
        fos.close();
    

    【讨论】:

    • Apparently bodyPart 应先转换为MimeBodyPart,如:((MimeBodyPart) bodyPart).saveFile("/tmp/" + bodyPart.getFileName());
    【解决方案4】:

    您可以简单地使用 Apache Commons Mail API MimeMessageParser - getAttachmentList() 以及 Commons IO 和 Commons Lang。

    MimeMessageParser parser = ....
    parser.parse();
    for(DataSource dataSource : parser.getAttachmentList()) {
    
        if (StringUtils.isNotBlank(dataSource.getName())) {}
    
            //use apache commons IOUtils to save attachments
            IOUtils.copy(dataSource.getInputStream(), ..dataSource.getName()...)
        } else {
            //handle how you would want attachments without file names
            //ex. mails within emails have no file name
        }
    }
    

    【讨论】:

      【解决方案5】:

      返回带有附件的身体部位列表。

      @Throws(Exception::class)
          fun getAttachments(message: Message): List<BodyPart>{
              val content = message.content
              if (content is String) return ArrayList<BodyPart>()
              if (content is Multipart) {
                  val result: MutableList<BodyPart> = ArrayList<BodyPart>()
                  for (i in 0 until content.count) {
                      result.addAll(getAttachments(content.getBodyPart(i)))
                  }
                  return result
              }
              return ArrayList<BodyPart>()
          }
      
          @Throws(Exception::class)
          private fun getAttachments(part: BodyPart): List<BodyPart> {
              val result: MutableList<BodyPart> = ArrayList<BodyPart>()
      
              if (Part.ATTACHMENT == part.disposition && !part.fileName.isNullOrBlank()){
                  result.add(part)
              }
      
              val content = part.content
              if (content is Multipart) {
                  for (i in 0 until (content ).count) {
                      val bodyPart = content.getBodyPart(i)
                      result.addAll(getAttachments(bodyPart)!!)
                  }
              }
              return result
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-14
        • 1970-01-01
        • 1970-01-01
        • 2016-05-13
        • 2017-08-08
        • 2013-04-13
        相关资源
        最近更新 更多