2015-11-18 77 views
1

我需要與其中一個郵件服務器進行交換,使用RFC3030作爲大型MIME郵件。 原始任務是:如果MIME消息大小> 80MB,則需要使用RFC3030。大郵件和二進制MIME郵件的JavaMail傳輸(rfc3030)

我怎麼理解,JavaMail不能做到「從盒子裏」?

也許我可以爲實現RFC3030的JavaMail創建一些處理程序或擴展?

請幫忙。我不知道該怎麼辦。

回答

0

快速查看SMTPTransport證實:普通老式的JavaMail不支持BDAT,它總是會嘗試使用DATA命令發送這樣的:

this.message.writeTo(data(), ignoreList); 
finishData(); 

如果你不害怕(,沒有任何法律理由不)與核心JDK類鼓搗,你可以覆蓋的方法數據()和finishData(),因爲它們是從here既保護(源代碼):

/** 
* Send the <code>DATA</code> command to the SMTP host and return 
* an OutputStream to which the data is to be written. 
* 
* @since JavaMail 1.4.1 
*/ 
protected OutputStream data() throws MessagingException { 
    assert Thread.holdsLock(this); 
    issueSendCommand("DATA", 354); 
    dataStream = new SMTPOutputStream(serverOutput); 
    return dataStream; 
} 

/** 
* Terminate the sent data. 
* 
* @since JavaMail 1.4.1 
*/ 
protected void finishData() throws IOException, MessagingException { 
    assert Thread.holdsLock(this); 
    dataStream.ensureAtBOL(); 
    issueSendCommand(".", 250); 
} 

爲了支持RFC3030,我d建議你從開始將整個消息緩存到ByteArrayOutputStream,您需要確定要發送的消息的大小。如果「小」 - >按照SMTPTransport那樣做。如果「大」,將字節拆分爲塊並以BDAT的樣式發送它們。我建議用0 lentgh LASTBDAT和代碼

protected void finishData() throws IOException, MessagingException { 
    assert Thread.holdsLock(this); 
    dataStream.ensureAtBOL(); 
    issueSendCommand("BDAT 0 LAST", 250); 
} 

結束 - 編輯 -

這裏是一個非常單純的第一方式,很多事情做的更好。最重要的是outputStream發送出大塊數據,而message.writeTo()繼續填充它的塊即用實現。在內存佔用方面,填充一個大字節[]只是爲了將其分成多個塊而非常糟糕。但由於所有的分塊和發送都在一個地方發生,讀取這種方式更容易。請注意,此示例使用反射訪問Oracle的SMTPTransport中的serverOutput字段。因此,隨着JavaMail的任何新版本的發佈,它可能會隨時發佈,而不會有任何警告。此外,我的異常處理現在不遵循RFC-3030,因爲如果BDAT失敗,則不執行RSET。

package de.janschweizer; 

import java.io.BufferedReader; 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.OutputStream; 
import java.io.PrintWriter; 
import java.io.StringReader; 
import java.lang.reflect.Field; 

import javax.mail.MessagingException; 
import javax.mail.Session; 
import javax.mail.URLName; 

import com.sun.mail.smtp.SMTPOutputStream; 

public class SMTPTransport extends com.sun.mail.smtp.SMTPTransport { 

    //We can have our own copy - it's only used in the methods we override anyways. 
    private SMTPOutputStream dataStream; 

    private ByteArrayOutputStream baos; 

    public SMTPTransport(Session session, URLName urlname, String string, boolean bool) { 
     super(session, urlname, string, bool); 
    } 

    public SMTPTransport(Session session, URLName urlname) { 
     super(session, urlname);    
    } 

    protected OutputStream data() throws MessagingException { 
     assert(Thread.holdsLock(this)); 
     if(!supportsExtension("CHUNKING")) { 
      return super.data(); 
     } 
     baos = new ByteArrayOutputStream(); 
     this.dataStream = new SMTPOutputStream(baos);  
     return this.dataStream; 
    } 

    protected void finishData() throws IOException, MessagingException { 
     assert(Thread.holdsLock(this)); 
     if(!supportsExtension("CHUNKING")) { 
      super.finishData(); 
      return; 
     } 
     this.dataStream.ensureAtBOL(); 
     dataStream.flush(); 
     BufferedReader br = new BufferedReader(new StringReader(new String(baos.toByteArray()))); 

     try { 
      //BAD reflection hack 
      Field fServerOutput = com.sun.mail.smtp.SMTPTransport.class.getDeclaredField("serverOutput"); 
      fServerOutput.setAccessible(true); 
      OutputStream os = (OutputStream)fServerOutput.get(this); 

      //Do the Chunky 
      ByteArrayOutputStream bchunk = new ByteArrayOutputStream(); 
      PrintWriter pw = new PrintWriter(bchunk); 
      String line = br.readLine(); 
      int linecount = 0; 
      while(line != null) { 
       pw.println(line); 
       if(++linecount % 5000 == 0) { 
        pw.flush(); 
        byte[] chunk = bchunk.toByteArray(); 
        sendChunk(os, chunk); 
        bchunk = new ByteArrayOutputStream(); 
        pw = new PrintWriter(bchunk); 
       } 
       line = br.readLine(); 
      } 
      pw.flush(); 
      byte[] chunk = bchunk.toByteArray(); 
      sendLastChunk(os, chunk);   
     } catch (Exception e) { 
      throw new MessagingException("ReflectionError", e); 
     } 
    } 

    private void sendChunk(OutputStream os, byte[] chunk) throws MessagingException, IOException { 
     sendCommand("BDAT "+chunk.length); 
     os.write(chunk); 
     os.flush();  
     int rc = readServerResponse(); 
     if(rc != 250) { 
      throw new MessagingException("Something very wrong"); 
     } 
    } 

    private void sendLastChunk(OutputStream os, byte[] chunk) throws MessagingException, IOException { 
     sendCommand("BDAT "+chunk.length+" LAST"); 
     os.write(chunk); 
     os.flush();  
     int rc = readServerResponse(); 
     if(rc != 250) { 
      throw new MessagingException("Something very wrong"); 
     } 
    } 
} 

有了這個META-INF/javamail.providers

protocol=smtp; type=transport; class=de.janschweizer.SMTPTransport; vendor=Jan Schweizer; 
+0

是的,這可能是正確的做法,但代碼應該檢查服務器是否支持做任何的在此之前的CHUNKING擴展。此外,您應該能夠繼承SMTPTransport,創建自己的Transport類型,並使用javamail.providers文件將其打包到自己的jar文件中,並且可以與JavaMail一起使用,而不必更改JavaMail本身。 –

+0

@BillShannon是的,創建自己的實現和子類化聽起來不錯。太糟糕了,所有的dataStream,serverOutput和issueSendCommand都是私有的。沒有完整的源代碼副本或真正糟糕的反思黑客,這是一個死衚衕。 – Jan

+0

@BillShannon刮痕。反正我們只需要serverOutput。以受保護的方式訪問該文件可能會更好 – Jan