使用JavaMail發送郵件-從FTP讀取圖片並添加到郵件正文發送


記錄一下,使用JavaMail發送郵件。

業務分析

最近工作需要,需要從FTP讀取圖片內容,添加到郵件正文發送。發送郵件正文,添加附件采用Spring的MimeMessageHelper對象來完成,添加圖片也將采用MimeMessageHelper來完成。

查看博客發現MimeMessageHelper有addInline API,里面包含三個參數contentId,InputStreamSource,content-type,以下是文檔里的解釋:

public void addInline(java.lang.String contentId,InputStreamSource inputStreamSource,java.lang.String contentType) throws MessagingException Add an inline element to the MimeMessage, taking the content from an org.springframework.core.InputStreamResource, and specifying the content type explicitly. You can determine the content type for any given filename via a Java Activation Framework's FileTypeMap, for example the one held by this helper. Note that the InputStream returned by the InputStreamSource implementation needs to be a fresh one on each call, as JavaMail will invoke getInputStream() multiple times. NOTE: Invoke addInline after setText; else, mail readers might not be able to resolve inline references correctly. Parameters: contentId - the content ID to use. Will end up as "Content-ID" header in the body part, surrounded by angle brackets: e.g. "myId" -> "<myId>". Can be referenced in HTML source via src="cid:myId" expressions. inputStreamSource - the resource to take the content from contentType - the content type to use for the element Throws: MessagingException - in case of errors See Also: setText(java.lang.String), getFileTypeMap(), addInline(String, org.springframework.core.io.Resource), addInline(String, javax.activation.DataSource)

百度翻譯過來大概意思是:

contentId:要使用的ContentId。將在正文部分以“content id”標題結尾,並用尖括號包圍:例如“myid”->“<myid>”。也可以通過src=“cid:myid”表達式應用在HTML源代碼中

inputStreamSource:需要傳入的圖片資源。

contentType:將會被元素使用的contentType,內容類型,一般是指網頁中存在的Content-Type,用於定義網絡文件的類型和網頁的編碼,決定文件接收方將以什么形式、什么編碼讀取這個文件。

所以,contentId自己命名后,需要將這個id添加到<img src="cid:id">中被引,content-type在這里因為傳輸的是圖片,需要選擇image/jpeg,而inputStreamSource來源自ftp讀取結果。

業務實現

接下主要問題是如何讀取ftp里的圖片信息,並將圖片信息轉換成inputStreamSource。因此分為兩步來實現:

(1)連接ftp

 參考代碼,主要參考了博客https://www.cnblogs.com/l412382979/archive/2018/01/15/8288030.html,執行方法后,返回保存InputStream對象和FTPClient對象的Map,給下一步使用。

    /** * Name:get InputStream from ftp * @author yangchaolin */
    public static Map<String,Object> getDefectImageFromFtpForACCM(String sectionName) throws Exception{ InputStream is=null; FTPClient ftpClient=new FTPClient(); Map<String,Object> map=new HashMap<String,Object>(); try{ //創建連接
            ftpClient.connect("ftp地址",端口號); ftpClient.login("用戶名", "密碼"); //驗證用戶名和密碼
            int reply=ftpClient.getReplyCode(); if(!FTPReply.isPositiveCompletion(reply)){ log.info("連接ftp失敗,用戶名或密碼錯誤!"); ftpClient.disconnect(); }else{ log.info("連接ftp成功!"); } }catch(SocketException e){ log.info("ftp IP地址可能錯誤"); throw new CustomException("ftp IP地址可能錯誤!"); }catch(IOException e){ log.info("ftp 端口錯誤"); throw new CustomException("ftp 端口錯誤!"); } try{ ftpClient.setDataTimeout(60000); ftpClient.setConnectTimeout(60000); ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); ftpClient.setControlEncoding("UTF-8");//支持中文
            ftpClient.setBufferSize(8*1024); //獲取指定文件的InputStream
            ftpClient.changeWorkingDirectory("文件目錄");//目錄最前面不能以'/'開頭,否則會讀不到內容
            is=ftpClient.retrieveFileStream(sectionName); }catch(FileNotFoundException e){ log.info("沒有找到文件"); throw new CustomException("沒有找到文件"); }catch(SocketException e){ log.info("連接ftp失敗"); throw new CustomException("連接ftp失敗"); }catch(IOException e){ log.info("文件讀取錯誤"); throw new CustomException("文件讀取錯誤"); } map.put("inputStream", is); map.put("ftpClient", ftpClient); return map; }
(2)將圖片信息轉化成inputStreamSource

 這里截取了部分代碼,先將InputStream轉換成字節數組,然后將字節數組作為參數傳入方法ByteArrayResource()中,得到InputeStreamSource對象,InputeStreamSource對象將傳入MimeMessageHelper對象中,作為添加郵件中圖片使用。

//讀取ftp上圖片內容
try { Map<String,Object> map=getDefectImageFromFtpForACCM("Mountain.jpg"); InputStream isOfImage=(InputStream) map.get("inputStream"); FTPClient ftpClient=(FTPClient) map.get("ftpClient"); if(isOfImage!=null){ byte[] bytes=IOUtils.toByteArray(isOfImage);//將圖片輸入流轉化為字節數組 //isOfImage.read(bytes);
                        issOfImage=new ByteArrayResource(bytes); log.info("圖片byte數組大小為:"+bytes.length); //將inputStream關閉后才能執行completePendingCommand方法
 isOfImage.close(); ftpClient.completePendingCommand(); ftpClient.disconnect(); } } catch (Exception e1) { throw new CustomException("讀取ftp圖片失敗!"); }
(3)將InputStreamSource傳入MimeMessageHelper中發送帶圖片的郵件,圖片顯示在正文中

這里截取了部分代碼,helper就是MimeMessageHelper對象,其中defectImage就是contentId,將會用在<img src="cid:defectImage">標簽中,相當如一個標識符(html郵件部分代碼不做展示),通過它可以找到圖片資源。content-type暫時定義為image/jepg,這個需要參考具體的標准表,因為本次測試使用的是jpg格式圖片,所以選擇它。

//------------------------------測試用 helper.setText(message.toString(),true);//HTML郵件 
//image就是InputStreamSource對象
helper.addInline("defectImage", image, "image/jpeg"); //content-type暫定義為image/jpeg,代表將已圖片解碼 log.info("郵件內容為:"+message.toString()); //
------------------------------測試用

郵件發送效果

 

總結

在添加郵件到郵件正文的過程中,發生了如下問題:

(1)圖片只發送了一部分,沒有全部發送完成。查看原因發現是代碼inputStream轉化成byte[]數組時,長度出現了問題,導致只傳輸了部分圖片,使用IOUtils.toByteArray() API后,長度正常。

(2)圖片在foxmail顯示正常,但是在chrome瀏覽器顯示不出圖片,查看發現content-type設置為"text/html",將其修改為"image/jpeg"后顯示正常。

(3)執行InputStream流關閉后,執行completePendingCommand()方法出錯,發現是先關閉了ftp的連接,導致無法執行。completePendingCommand()會等FTPClient返回226 Transfer complete,但是FTPClient只有在接受到InputStream執行close方法時,才會返回。所以先要執行InputStream的close方法,再執行completePendingCommand,最后再關閉ftp連接。參考自:https://ask.csdn.net/questions/166657


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM