Java는 EML 메일 형식 파일을 구문 분석합니다.

기본 소개

전자 메일에 대한 수요는 항상 전자 메일 보내기 또는 받기를 기반으로 합니다.과거 관련된 기술 선택에는 Java Mail, Apache Commons 전자 메일 및 Spring Mail이 포함됩니다.작업의 필요성으로 인해 eml 형식의 파일을 파싱합니다.에 대해 학습한 후 그것, 자바 사용 eml 형식 파일의 구현을 분석하기 위해 소위 eml 형식은 RFC822 및 후속 확장을 따르는 Outlook에서 Microsoft가 사용하는 파일 형식이며 다양한 이메일 소프트웨어 (로컬 이메일 파일)의 공통 형식이되었습니다 저장) 소스는 영문 E-mail의 축약형이며 Outlook 또는 Foxmail 및 Notes와 같은 다양한 로컬 이메일 클라이언트에서 열 수 있습니다.

여러 정보 백과사전을 통해 Java Mail 및 Mime4J(Apache James 하위 프로젝트 모듈) 분석을 주로 사용할 수 있음을 발견했습니다. 에서 완전하고 안정적이며 안전하고 확장 가능한 메일 서버 통합. James는 내부 프로젝트(Server, Mailet, Mailbox, Protocols, MPT)와 외부 프로젝트(Hupa, Mime4J, jSieve, jSPF, jDKIM)로 구성되며, 그 중 Mime4J는 다음 그림과 같이 메일 데이터 파일을 구문 분석하는 구현체입니다. :

Apache James Mime4J는 일반 RFC822 및 MIME에서 이메일 스트림 형식에 대한 구문 분석기를 제공합니다. 파서는 콜백 메커니즘을 사용하여 엔터티 헤더, 본문 시작 등과 같은 구문 분석 이벤트를 보고합니다. SAX Corporation XML 파서 인터페이스에 익숙하다면 mime4j를 시작하는 데 문제가 없을 것입니다. Mime4j는 또한 메시지 클래스를 사용하여 이메일을 작성하는 데 사용할 수 있습니다. 이 도구를 사용하면 mime4j가 자동으로 필드 및 본문 디코딩을 처리하고 대용량 임시 파일을 첨부합니다.

구문 분석 구현

(1) QQ 메일박스를 사용하여 이메일을 편집하여 발송하며 모든 곳에 로컬 eml 파일이 있습니다.이메일의 정보(추출 가능한 매개변수)에는 다음이 포함됩니다.

A. 이메일 제목:

B. 이메일 콘텐츠, 콘텐츠 부분은 일반 텍스트 및 리치 텍스트일 수 있으며 리치 텍스트에는 HTML 파일, 콘텐츠 영역의 로컬 사진 등이 포함됩니다.

다. 받는 사람은 여러 명일 수 있으며 받는 사람은 닉네임과 실제 이메일 주소를 구분합니다.

D. CC, 수령인과 일치;

E. 숨은 참조 발신자와 수신자가 동일합니다.

F. 첨부 파일, 여러 개의 첨부 파일이 있을 수 있습니다.

G. 시간대 문제가 있는지 여부를 보내는 시간;

H. 메일 크기, 메일 파일의 크기

I. 메시지 ID 메일의 고유 ID입니다.

(2) 이메일 내용은 다음 그림과 같이 여러 수신자와 참조, 여러 이메일로 특별히 구성되어 있으며 이메일 내용에는 HTML 리치 텍스트 단락과 로컬 이미지 파일이 포함되어 있습니다.

(3) maven 종속성 가져오기(버전 0.8.9는 2023년 1월 초에 릴리스됨) 좌표 종속성 실행 후 apache-mime4j-examples 좌표를 가져오면 여러 종속 모듈을 직접 가져올 수 있음이 밝혀졌으므로 실제 응용 프로그램에서 고려해야 합니다. 다른 모듈에 따라 필요에 따라 emamples 및 commons-logging 종속성을 제외하고 참조 좌표는 다음과 같습니다.

<!-- https://mvnrepository.com/artifact/org.apache.james/apache-mime4j-examples -->

<dependency>
    <groupId>org.apache.james</groupId>
    <artifactId>apache-mime4j-examples</artifactId>
    <version>0.8.9</version>
</dependency>

(4) 구문 분석 구현 예:

package cn.chendd.eml;
 
 /**
  * Eml文件解析数据对象
  *
  * @author chendd
  * @date 2023/2/11 21:40
  */
 @Data
 public class EmlEntry {
 
     /**
      * 原始message对象
      */
     @JSONField(serialize = false)
     private Message message;
 
     /**
      * 消息ID
      */
     private String messageId;
 
     /**
      * 邮件主题
      */
     private String subject;
 
     /**
      * 纯文本邮件内容
      */
     private String textContent;
 
     /**
      * 富文本邮件内容
      */
     private String htmlContent;
 
     /**
      * 邮件附件
      */
     private List<MutableTriple<String , Long , InputStream>> attachments = Lists.newArrayList();
 
     /**
      * 发件人
      */
     private String from;
 
     /**
      * 收件人
      */
     private List<Pair<String , String>> to;
 
     /**
      * 抄送人
      */
     private List<Pair<String , String>> cc;
 
     /**
      * 密送人
      */
     private List<Pair<String , String>> bcc;
 
     /**
      * 邮件时间
      */
     private String dateTime;
 
}
package cn.chendd.eml;
 
 /**
  * 基本的eml文件解析示例
  *
  * @author chendd
  * @date 2023/2/11 19:26
  */
 public class EmlBasicTest {
 
     public static void main(String[] args) {
 
         try (InputStream inputStream = EmlBasicTest.class.getResourceAsStream("/Java解析Eml格式文件示例.eml")) {
             Message message = Message.Builder.of(inputStream).build();
             EmlEntry entry = new EmlEntry();
             entry.setMessage(message);
             entry.setMessageId(message.getMessageId());
             entry.setSubject(message.getSubject());
             entry.setFrom(address2String(message.getFrom()));
             entry.setTo(address2List(message.getTo()));
             entry.setCc(address2List(message.getCc()));
             entry.setBcc(address2List(message.getBcc()));
             TimeZone timeZone = TimeZone.getTimeZone(ZoneId.of("GMT"));
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             sdf.setTimeZone(timeZone);
             entry.setDateTime(sdf.format(message.getDate()));
             MultipartImpl body = (MultipartImpl) message.getBody();
             List<Entity> bodyParts = body.getBodyParts();
             //邮件附件和内容
             outputContentAndAttachments(bodyParts , entry);
             System.out.println(JSON.toJSONString(entry , true));
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
 
     /**
      * 递归处理邮件附件(附件区域附件、内容中的base64图片附件)、邮件内容(纯文本、html富文本)
      * @param bodyParts 邮件内容体
      * @param entry 数据对象
      * @throws IOException 异常处理
      */
     private static void outputContentAndAttachments(List<Entity> bodyParts , EmlEntry entry) throws IOException {
         for (Entity bodyPart : bodyParts) {
             Body bodyContent = bodyPart.getBody();
             String dispositionType = bodyPart.getDispositionType();
             if (ContentDispositionField.DISPOSITION_TYPE_ATTACHMENT.equals(dispositionType)) {
                 //正常的附件文件
                 BinaryBody binaryBody = (BinaryBody) bodyContent;
                 entry.getAttachments().add(MutableTriple.of(bodyPart.getFilename() , binaryBody.size() , binaryBody.getInputStream()));
                 continue;
             }
             if (bodyContent instanceof TextBody) {
                 //纯文本内容
                 TextBody textBody = (TextBody) bodyContent;
                 ContentTypeFieldLenientImpl contentType = (ContentTypeFieldLenientImpl) bodyPart.getHeader().getField(HttpHeaders.CONTENT_TYPE);
                 String mimeType = contentType.getMimeType();
                 //可动态获取内容的编码,按编码转换
                 if (MediaType.PLAIN_TEXT_UTF_8.toString().startsWith(mimeType)) {
                     entry.setTextContent(IOUtils.toString(textBody.getReader()));
                 }
                 if (MediaType.HTML_UTF_8.toString().startsWith(mimeType)) {
                     entry.setHtmlContent(IOUtils.toString(textBody.getReader()));
                 }
             } else if (bodyContent instanceof Multipart) {
                 MultipartImpl multipart = (MultipartImpl) bodyContent;
                 outputContentAndAttachments(multipart.getBodyParts() , entry);
             } else if (bodyContent instanceof BinaryBody) {
                 BinaryBody binaryBody = (BinaryBody) bodyContent;
                 outputContentInAttachment(bodyPart.getHeader(), binaryBody, entry);
             } else {
                 System.err.println("【是否还存在未覆盖到的其它内容类型场景】?");
             }
         }
     }
 
     /**
      * 处理内容中的图片附件
      *
      * @param header      附件头信息对象
      * @param binaryBody  附件对象
      * @param entry 解析数据对象
      */
     private static void outputContentInAttachment(Header header, BinaryBody binaryBody, EmlEntry entry) throws IOException {
         Field contentIdField = header.getField(FieldName.CONTENT_ID);
         Field typeField = header.getField(FieldName.CONTENT_TYPE);
         if (typeField instanceof ContentTypeField) {
             ContentTypeField contentTypeField = (ContentTypeField) typeField;
             if (contentTypeField.getMediaType().startsWith(MediaType.ANY_IMAGE_TYPE.type())) {
                 try (InputStream inputStream = binaryBody.getInputStream()) {
                     String base64 = Base64.getEncoder().encodeToString(IOUtils.toByteArray(inputStream));
                     String cid = StringUtils.substringBetween(contentIdField.getBody(), "<", ">");
                     String content = StringUtils.replace(entry.getHtmlContent(),
                             "cid:" + cid, "data:" + contentTypeField.getMimeType() + ";base64," + base64);
                     entry.setHtmlContent(content);
                 }
             }
         }
     }
 
     /**
      * 转换邮件联系人至String
      * @param addressList 邮件联系人
      * @return String数据
      */
     private static String address2String(MailboxList addressList) {
         if (addressList == null) {
             return StringUtils.EMPTY;
         }
         for (Address address : addressList) {
             return address.toString();
         }
         return StringUtils.EMPTY;
     }
 
     /**
      * 转换邮件联系人至list集合
      * @param addressList 邮件联系人
      * @return list集合
      */
     private static List<Pair<String , String>> address2List(AddressList addressList) {
         List<Pair<String , String>> list = Lists.newArrayList();
         if (addressList == null) {
             return list;
         }
         for (Address address : addressList) {
             Mailbox mailbox = (Mailbox) address;
             list.add(Pair.of(mailbox.getName() , mailbox.getAddress()));
         }
         return list;
     }
 }

분석 결과

(파싱된 JSON 결과)

(HTML 단락은 파일로 저장됩니다)

기타 지침

(1) eml 형식 파일은 메모장, 메모장++ 및 기타 도구로 열 수 있는 일반 텍스트 파일이므로 내용을 볼 때 필요에 따라 사용자 지정 분석을 수행할 수도 있습니다.

(2) 실제 적용은 확실히 이보다 더 복잡합니다.이 기사는 여러 지식 세부 사항이 풍부한 예일 뿐이며 작업의 실제 처리는 훨씬 더 복잡합니다.

(3) 일부 시나리오에서 첨부 파일 이름은 특별한 코드 변환이 필요하며 실제 첨부 파일 이름은 병합 및 코드 변환이 필요한 이메일의 소스 파일에서 여러 세그먼트로 나뉩니다.

(4) 이메일에는 특별한 처리가 필요한 회신 이메일 및 전달 이메일과 같은 여러 이메일 교환이 포함되어 있습니다.

(5) 이메일의 콘텐츠 부분은 다양한 콘텐츠 유형을 포함하고 있으며, 다양한 파싱 적응 프로그램을 제공할 필요가 있다. 서식 있는 텍스트를 먼저 구문 분석한 다음 이진 콘텐츠 본문을 기다려야 합니다.)

(6) 이 문서의 코드는 참조용이며 직접 사용해서는 안 됩니다. 콘텐츠 유형별 구문 분석 구현은 팩토리 모델을 기반으로 하는 다중 구문 분석 적응을 기반으로 해야 합니다. 샘플 프로젝트의 소스 코드를 참조하십시오. 아래 개인 사이트 기사에 설명되어 있습니다.

(7) 자세한 내용은 https://www.chendd.cn/blog/article/1624252901639442434.html 을 참조하십시오.

추천

출처blog.csdn.net/haiyangyiba/article/details/129086959