001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.mail; 017 018import org.opengion.fukurou.system.Closer ; 019import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 020 021import javax.mail.MessagingException; 022import javax.mail.Part; 023import javax.mail.BodyPart; 024import javax.mail.Multipart; 025import java.io.File; 026import java.io.InputStream; 027import java.io.FileOutputStream; 028import java.io.IOException; 029import java.util.List; 030import java.util.ArrayList; 031import java.util.Set; 032import java.util.HashSet; 033 034/** 035 * メール添付ファイル処理クラス 036 * 037 * このクラスは、添付ファイルを処理するためのクラスです。 038 * 添付ファイルは、マルチパートに含まれている為、再帰的に探す必要があります。 039 * 040 * @version 4.0 041 * @author Kazuhiko Hasegawa 042 * @since JDK5.0, 043 */ 044public class MailAttachFiles { 045 private final List<Part> files ; 046 private final String[] names ; 047 048 /** 049 * Partオブジェクトを受け取るコンストラクター 050 * 051 * 内部変数の初期化を行います。 052 * 053 * @param part Partオブジェクト 054 */ 055 public MailAttachFiles( final Part part ) { 056 files = new ArrayList<>(); 057 names = makeNames( part ); 058 } 059 060 /** 061 * 添付ファイルの名称を文字列配列として求めます。 062 * 063 * @return 添付ファイルの名称を文字列配列 064 */ 065 public String[] getNames() { 066 String[] rtn = null ; 067 068 if( names != null ) { rtn = names.clone(); } 069 070 return rtn ; 071 } 072 073 /** 074 * 添付ファイルの名称を文字列配列として求めます。 075 * 076 * この処理の中で、添付ファイルを持つ Part を見つけて内部配列(List)に登録します。 077 * ファイル名が未指定の場合は、"noNameFile" + i + ".tmp" というファイル名をつけます。 078 * i は、添付ファイルの連番です。 079 * また、同一添付ファイル名が存在する場合は、頭に添付ファイルの連番を付加して、 080 * ファイル名としてユニーク化します。 081 * 082 * @og.rev 4.3.3.5 (2008/11/08) 日本語添付ファイルが処理できるように修正 083 * 084 * @param part Partオブジェクト 085 * 086 * @return 添付ファイルの名称を文字列配列 087 */ 088 private String[] makeNames( final Part part ) { 089 final String[] nms; 090 try { 091 final Set<String> set = new HashSet<>(); 092 093 fileSearch( part ); 094 nms = new String[files.size()]; 095 for( int i=0; i<nms.length; i++ ) { 096 final String name = files.get(i).getFileName(); 097 if( name == null ) { // message か、ファイル名未指定のケース 098 nms[i] = "noNameFile" + i + ".tmp" ; 099 } 100 // 4.3.3.5 (2008/11/08) 日本語添付ファイルが処理できるように修正 101 else { 102 nms[i] = MailMessage.mimeDecode( name ); 103 } 104 105 // 重複チェック 106 if( !set.add( nms[i] ) ) { 107 nms[i] = i + "_" + nms[i] ; // 重複時に名称変更します。 108 } 109 } 110 } 111 catch( final MessagingException ex ) { 112 final String errMsg = "メッセージ情報のハンドリングに失敗しました。" 113 + ex.getMessage(); // 5.1.8.0 (2010/07/01) errMsg 修正 114 throw new OgRuntimeException( errMsg,ex ); 115 } 116 catch( final IOException ex ) { 117 final String errMsg = "テキスト情報の取り出しに失敗しました。" 118 + ex.getMessage(); // 5.1.8.0 (2010/07/01) errMsg 修正 119 throw new OgRuntimeException( errMsg,ex ); 120 } 121 return nms; 122 } 123 124 /** 125 * 添付ファイルが存在するかどうかをサーチします。 126 * 127 * 添付ファイルは、マルチパートで指定されると、再帰的に検索する必要が 128 * 出てきます。このメソッドでは、再帰的にファイルかどうかを検索し、 129 * ファイルであれば、内部変数(List)に追加(add)していきます。 130 * 131 * @param part Partオブジェクト 132 * 133 * @return 再帰検索終了 true 134 * @throws MessagingException javax.mail 関連のエラーが発生したとき 135 * @throws IOException 入出力エラーが発生したとき 136 * 137 */ 138 private boolean fileSearch( final Part part ) throws MessagingException ,IOException { 139 if( part.isMimeType( "multipart/*" ) ) { 140 final Multipart mpt = (Multipart)part.getContent(); 141 142 final int count = mpt.getCount(); 143 for( int i=0; i<count; i++ ) { 144 final BodyPart bpt = mpt.getBodyPart(i); 145 fileSearch( bpt ); 146 } 147 } 148 else { 149 if( part.isMimeType( "message/*" ) || 150 part.getFileName() != null || 151 Part.INLINE.equalsIgnoreCase( part.getDisposition() ) ) { 152 files.add( part ); 153 } 154 } 155 return true; 156 } 157 158 /** 159 * 添付ファイルを指定のフォルダにセーブします。 160 * 161 * 内部変数List の 添付ファイルを持つ Part について、ファイルを抜出し、 162 * 指定のディレクトリに保存していきます。 163 * ファイル名は、基本的に添付ファイル名そのものですが、 164 * 同一名称の添付ファイルが複数登録されている場合は、その重複ファイルの番号を 165 * 頭につけ、番号 + "_" + 添付ファイル名 として、ユニーク化します。 166 * 167 * ※ ディレクトリの作成に失敗した場合、RuntimeException が throw されます。 168 * 169 * @param dir セーブするディレクトリ (nullの場合は、セーブしない) 170 * @param newNm セーブするファイル名 (nullの場合は、非重複化された添付ファイル名) 171 * @param fno 添付ファイルの番号 172 */ 173 public void saveFileName( final String dir, final String newNm, final int fno ) { 174 if( dir == null ) { return ; } // ファイルをセーブしない。 175 176 final File fileDir = new File( dir ); 177 if( !fileDir.exists() ) { 178 final boolean isOk = fileDir.mkdirs(); 179 if( ! isOk ) { 180 final String errMsg = "ディレクトリの作成に失敗しました。[" + dir + "]"; 181 throw new OgRuntimeException( errMsg ); 182 } 183 } 184 185 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..; 186 final String newName = newNm == null ? names[fno] : newNm ; 187 188 InputStream input = null; 189 FileOutputStream output = null; 190 191 try { 192 final Part prt = files.get( fno ); 193 input = prt.getInputStream(); 194 output = new FileOutputStream( new File( fileDir,newName ) ); 195 final byte[] buf = new byte[1024]; 196 int len; 197 while( (len = input.read(buf)) != -1 ) { 198 output.write( buf,0,len ); 199 } 200 } 201 catch( final MessagingException ex ) { 202 final String errMsg = "メッセージオブジェクトの操作中にエラーが発生しました。" 203 + "dir=[" + dir + "], file=[" + newName + "], No=[" + fno + "]" 204 + ex.getMessage(); // 5.1.8.0 (2010/07/01) errMsg 修正 205 throw new OgRuntimeException( errMsg,ex ); 206 } 207 catch( final IOException ex ) { 208 final String errMsg = "添付ファイルの取り扱い中にエラーが発生しました。" 209 + "dir=[" + dir + "], file=[" + newName + "], No=[" + fno + "]" 210 + ex.getMessage(); // 5.1.8.0 (2010/07/01) errMsg 修正 211 throw new OgRuntimeException( errMsg,ex ); 212 } 213 finally { 214 Closer.ioClose( output ); 215 Closer.ioClose( input ); 216 } 217 } 218}