【Java mail】解析邮件的时候死循环

  • Post author:
  • Post category:java


记一下遇到的一个bug



问题产生

有个读取邮件的定时任务,通过rabbitMQ异步操作的,今天突然卡住了,排查发现mq阻塞了,进一步排查发现有一封邮件解析的时候死循环了

最开始是解析邮件的时候org.apache.commons.mail.util.MimeMessageParser#getContent方法流处理死循环了,他的这个inputStream对象一直在刷新,无限长;

/**
     * Read the content of the input stream.
     *
     * @param is the input stream to process
     * @return the content of the input stream
     * @throws IOException reading the input stream failed
     */
    private byte[] getContent(final InputStream is)
        throws IOException
    {
        int ch;
        byte[] result;

        final ByteArrayOutputStream os = new ByteArrayOutputStream();
        final BufferedInputStream isReader = new BufferedInputStream(is);
        final BufferedOutputStream osWriter = new BufferedOutputStream(os);

        while ((ch = isReader.read()) != -1)
        {
            osWriter.write(ch);
        }

        osWriter.flush();
        result = os.toByteArray();
        osWriter.close();

        return result;
    }

之后我以为是解析包的问题 自己写了一套

List<DataSource> getAttachments(Message message) throws MessagingException, IOException {
        List<DataSource> result = new ArrayList<>();
        Part part = message;
        if (part.isMimeType("multipart/*")){
            Multipart multipart = (Multipart) part.getContent();
            for (int i = 0; i < multipart.getCount(); i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                String name = bodyPart.getDataHandler().getDataSource().getName();
                if (name != null){
                    result.add(bodyPart.getDataHandler().getDataSource());
                }
            }
        }
        return result;
    }

勉强通过这一步了

但是后面解析eml文件的时候又死循环了,一时间有点不知道怎么搞了,然后自己写了个读取流,芜湖~直接打到12w多

ByteArrayOutputStream out = new ByteArrayOutputStream();
        Map<String, String> map = new HashMap<>();
        try{
            InputStream inputStream = message.getInputStream();
            System.out.println("inputstream->available--->"+inputStream.available());
            byte[] buffer = new byte[1024];
            int read ;
            int i = 0;
            while ((read = inputStream.read(buffer)) != -1){
                out.write(buffer, 0, read);
                System.out.println(i++);
            }
//            message.writeTo(out);
            byte[] bytes = out.toByteArray();
            MD5 md5 = MD5.create();
            String md5Code = md5.digestHex16(bytes);
            RequestMultipartFile multipartFile = new RequestMultipartFile(message.getSubject()+".eml", bytes);
            String attachmentUrl = fileFeign.uploadFile(companyId, userId, "email", multipartFile);
            map.put("md5Code", md5Code);
            map.put("attachmentUrl", attachmentUrl);
        }finally {
            out.close();
        }

这个时候怀疑是邮件有问题,但是邮件有问题不知道有什么问题,也不好对邮件进行过滤,之后就去查Java mail的文档

结果真有发现


文档地址

Q: Does the IMAP provider cache the retrieved data?

A: The IMAP provider fetches the data for a message from the server only when necessary. (The javax.mail.FetchProfile can be used to optimize this). The header and bodystructure information, once fetched, is always cached within the Message object. However, the content of a bodypart is not cached. So each time the content is requested by the client (either using getContent() or using getInputStream()), a new FETCH request is issued to the server. The reason for this is that the content of a message could be potentially large, and if we cache this content for a large number of messages, there is the possibility that the system may run out of memory soon since the garbage collector cannot free the referenced objects. Clients should be aware of this and must hold on to the retrieved content themselves if needed.

果然 他在我们读的时候对流进行了处理,要自己处理好流的内容,但是也可以添加链接配置来解决这个问题

最后在链接配置中添加了javax.mail.FetchProfile为FALSE以后正常了

    /**
     * 生成邮箱连接
     */
    public static Store connectEmailServer(EmailConfigVo config) throws Exception {
        Properties props = new Properties();
        props.setProperty(StoreProtocolKey, config.getStoreProtocol());
        props.setProperty("mail.imap.host", config.getReceiveServerHost());
        props.setProperty("mail.properties.mail.smtp.port", config.getSmtpPort());
        props.setProperty("mail.imap.partialfetch","false");
        Session session = Session.getInstance(props);
        Store store = session.getStore();
        //连接服务器
        store.connect(config.getReceiveServerHost(), config.getUsername(), config.getPassword());
        return store;
    }



版权声明:本文为qq_25977455原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。