Javaでメールを解析する

Javaで80%を作成した最後のプロジェクトに、サーバーを通過するすべての文字のパーサーであるモジュールを追加する必要がありました。 モジュールの宗教的な動機は非常に奇妙ですが、詳細を共有したいと思います。



使用可能なものは次のとおりです。


CentOS上のDovecot配信サービスを備えたPostfixメールサーバー。 さて、JVM。



メッセージ構造


電子メール、そのコンポーネント、それらのおおよその構造、ヘッダー、MIMEタイプとは、 Wikipediaで人間的に説明されています。

さらに興味深いのは、サーバー上のレターのファイル名の構造です。 新しく作成された(クライアントによって要求されていない/読まれていない)手紙の名前の例:



1348142977.M852516P31269.mail.example.com,S=3309,W=3371
      
      







名前はフラグで構成されます。 フラグは、新しい文字を作成するとき、文字とそのサイズが示される「場所」、「いつ」、コンマで区切られます。





このうち、筆記時間(最初の10桁)が役に立ちました。 ただし、多くの場合、この時間はメッセージヘッダーの時間と異なる場合があるため、名前の時間はディレクトリ内のメッセージをフィルタリングするためだけに使用しました。



追加/クライアントフラグ


クライアントメールインターフェイス(以下、クライアントと呼びます)は、独自のフラグをレター名に追加できます。 クライアントフラグの開始は、記号「:」で示されます。



クライアントサーバーから新しいレター要求するとすぐに、要求がトランスポートに送信され、要求された各レターが「読み取り」ディレクトリに移動され、次のフラグとカンマで区切られた情報フラグ(2つのうちの1つ)が名前に追加されます:



サーバー上のメッセージが既に「読み取り」フォルダーにあるという事実にもかかわらず、ユーザーにはそれが新規として表示されます。 顧客は手紙の場所ではなく、フラグを読みます。

つまり、ユーザー自身が手紙(または彼との別のアクション)を開き、フラグ "S"(表示)が名前に追加された場合にのみ、視覚的に "読み上げられます"。 手紙に対するさまざまなアクションは、予想どおり、フラグを追加し、メモを参照してください。



例:

メールボックスの新しいメッセージがサーバーに届きました。名前は次のようになります。



 1348142977.M852516P31269.mail.example.com,S=3309,W=3371
      
      





私たちの背景では、 神は Outlookを禁止しています。Outlookは新しい文字のリストを要求し、サーバー上でそれらを「読み取り」ディレクトリに移動してフラグを追加するように言っています。



 1348142977.M852516P31269.mail.example.com,S=3309,W=3371:2,
      
      





次に、 削除してOutlookを開き、新しい文字をクリックすると、Sフラグが追加されます。



 1348142977.M852516P31269.mail.example.com,S=3309,W=3371:2,S
      
      





そして、それに答えて削除します:



 1348142977.M852516P31269.mail.example.com,S=3309,W=3371:2,SRT
      
      





ご覧のとおり、フラグはセパレータなしでリストされています。



注:一部のクライアントには、レターを「読み取り」フォルダーに構成する(移動しない)機能があります。 また、クライアントは、ドキュメントに示されていない「ニーズに応じて」フラグを追加することがありますが、特に注意していません。

より有用なフラグ情報: cr.yp.to/proto/maildir.html



そして少しのJava


文字の処理には、 javax.mailを使用しました。 抽象クラスjavax.mail.Messageが提供されていますが、この場合はjavax.mail.MimeMessageに制限されています。

モジュールはサーバー上でスピンするため、ローカルでメッセージにアクセスします(コード内のチェックと例外処理は省略されます)。



 //   properties   Session session = Session.getDefaultInstance(System.getProperties()); FileInputStream fis = new FileInputStream(pathToMessage); MimeMessage mimeMessage = new MimeMessage(session, fis);
      
      





これで、ASCIIで予期されるメッセージヘッダーを読み取ることができます。 ヘッダーが見つからない場合、nullを返します。 例:



 String messageSubject = mimeMessage.getSubject(); String messageId = mimeMessage.getMessageID();
      
      





受信者のリストを決定するために、引数としてMessage.RecipientTypeを取るgetRecipientsメソッドが提供されます。 このメソッドは、 Address型のオブジェクトの配列を返します。 たとえば、メッセージの受信者をリストします。



 for(Address recipient : mimeMessage.getRecipients(Message.RecipientType.TO)){ System.out.println(recipient.toString()); }
      
      





手紙の差出人を見つけるために、getFromメソッドがあります。 また、Address型のオブジェクトの配列を返します。 このメソッドは、「From」ヘッダーを読み取り、存在しない場合は「Sender」ヘッダーを読み取り、「Sender」が存在しない場合はnullを読み取ります。



 for(Address sender : mimeMessage.getFrom()){ System.out.println(sender.toString()); }
      
      





次に、メッセージの本文を分析します(ほとんどの場合、テキストと添付ファイルが必要です)。 複合(MIMEマルチパートメッセージ)にすることも、1ブロックのテキスト/プレーン形式のみを含めることもできます。 メッセージの本文が添付ファイル(テキストなし)のみで構成されている場合でも、マルチパートメッセージとしてマークされます。 RFC822によると、フォーマットはContent-Typeヘッダーのメッセージ本文(およびその部分)に指定されています。



  //        if(mimeMessage.isMimeType("multipart/mixed")){ // getContent()    ,   . //   - Object,    Multipart Multipart multipart = (Multipart) mimeMessage.getContent(); //       for(int i = 0; i < multipart.getCount(); i ++){ BodyPart part = multipart.getBodyPart(i); // html-   , "text/plain"  "text/html" (     html ),       : if(part.isMimeType("text/plain")){ System.out.println(part.getContent().toString()); } //    part  else if(Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition()){ //     .    ,  decode String fileName = MimeUtility.decodeText(part.getFileName()); //  InputStream InputStream is = part.getInputStream(); //    ,  -    .... } } } //          else if(mimeMessage.isMimeType("text/plain")){ System.out.println(mimeMessage.getContent().toString()); }
      
      







実際、それがすべてです。 資料が役に立てば幸いです。

また、oracle.comには便利なjavax.mail FAQがあります。



UPD:最初のコメントで述べたように、メッセージの本文部分は一緒にネストできます。 同じ場所のコメントでは、それらを整理するために2つの方法がレイアウトされています。



All Articles