.msgファイルをパースして中から添付ファイルを抜き出す
正確にはRubyというには微妙なんですが…。
前回までのあらすじ
- 選んだメール(本文と添付ファイル)をまとめてDLしてくるだけの簡単なお仕事はできるようになった
しかし、社内セキュリティGWの設定? のためかzipとかexeとか怪しい拡張子が付いている添付ファイルは一旦msgファイルに固められるという仕様のためそのままでは添付ファイルが見れない。
こういうメールが
メール ┗添付ファイル.zip
届いた時にはこうなっている
メール ┗ラップ.msg(\あやしいファイルだから注意してDLしてね/) ┗本来の添付ファイル.zip
なんとかRubyからmsgファイル内を解析して添付ファイルを引っこ抜いて保存ができまいか…というのが今回の問題。
結論
以下のライブラリを駆使して実現しました。
一応Rubyでmsgファイルを解析するライブラリも探してみたのですが、あまりないのかな? Downloading File SSFileDLL-MAPI-1.0-Source.zip - Outlook msg file parser DLL - SourceForge.JP こういうdllは見つけたのですが、MAPIがよくわからん上に説明を見る限りHasAttach(添付ファイルを持ってるか持ってないか?)くらいしかわからない? ようなのであきらめました。
あと、msgparserはJavaライブラリなので当初はJRubyで書けば何とかなるかなと思ってました。簡単なサンプルは Read an Outlook MSG file - Real's Java How-to このページのような感じ。が、今まで書いたコードをJRubyで実行すると失敗してしまったので方向転換;; RubyからJavaVMを操作できるrjbというライブラリを使う事に。
ダウンロード・インストール
- msgparser
- msgparser - Downloads... よりmsgparserをDL
- Apache POI - POIFS - Java implementation of the OLE 2 Compound Document formatやJTNEF - Java TNEF packageとかが必要になるのですが、上記のmsgparse.zipの中に全部入ってます*1
- distよりmasparser-X.XX.jarを、libよりpoi-X.X-YYYYMMDD.jarとtnef-X.X.X.jarを取得し、同じプロジェクト内にコピー。とりあえずlibフォルダを作ってそこに入れました
- msgparser - Downloads... よりmsgparserをDL
- rjb
gem install rjb
ソース
ソース全体は gosyujin/outlook_for_ruby · GitHub 。msgParse.rbが本体、ライブラリがlibに入っています。
使い方としてはMsgParseをnewしてinputMsgで.msgファイルのパス指定、saveFileで添付ファイルぶっこぬいて出力先に保存としたい。こんな感じで。
msg = MsgParse.new msg.inputMsg(MSGFILE) msg.saveFile(SAVEDIR)
という事でMsgParser*2クラスを作成。ソースは outlook_for_ruby/msgParse.rb at master · gosyujin/outlook_for_ruby · GitHub 。まずは初期化から。
include Rjb # JavaクラスのImport、Jarの読み込み # 初期化処理を行う def initialize #Rjb::load('./') initJavaClass() addJar() @msg = nil end # JavaのクラスをImportする def initJavaClass() @system = import("java.lang.System") # @string = import("java.lang.String") # @list = import("java.util.List") @fileOutputStream = import("java.io.FileOutputStream") end # Jarを読みこむ def addJar() Rjb::add_jar(File.expand_path('lib/tnef-1.3.1.jar')) Rjb::add_jar(File.expand_path('lib/poi-3.2-FINAL-20081019.jar')) Rjb::add_jar(File.expand_path('lib/msgparser-1.10.jar')) @msgParser = import("com.auxilii.msgparser.MsgParser") @fileAttachment = import("com.auxilii.msgparser.attachment.FileAttachment") end
java.lang.Systemとかjava.lang.Stringとか使う予定のあるクラスは全て明示的にimportしてやる必要がある。今回はjava.lang.Systemとjava.io.FileOutputStreamを使用する。
次にライブラリを読み込む。msgparserをDLしたときのtnef, poi, msgparserを追加する。追加後にcom.auxilii.msgparser.MsgParserとcom.auxilii.msgparser.attachment.FileAttachmentをimportする。※add_jarの順番でエラーになる。-classpathの記述? に順番って関係あるんだっけ?
これで今回使用したいクラスはRuby上から呼び出せるようになりました。次はファイル読み込み。
# .msgファイルを読みこむ def inputMsg(path) @msg = @msgParser.new.parseMsg(path) end
parseMsg(PATH)メソッドを使用し.msgファイルを読み込めるように。最後は.msgファイル内から添付ファイルをぶっこぬく!
# .msgファイルの添付ファイル数をカウントする def getAttachmentSize() @msg.getAttachments.size end # 添付ファイルをpathに保存する # 返り値は保存した添付ファイル名(の一つ) def saveFile(path) fileName = "" if getAttachmentSize() != 0 then for i in 0..getAttachmentSize - 1 file = @msg.getAttachments.get(i) begin fileName = file.getLongFilename rescue => ex puts "File name is including WAVE DASH?:#{ex}" fileName = file.getFilename end out = @fileOutputStream.new(path + fileName) out.write(file.getData) puts "■.msgファイル抽出:#{fileName}" out.close end else puts "no temp file." end return fileName end end
はじめにgetAttachments.get(i)で添付ファイルを取得します。次に保存する時のファイル名として添付ファイル名を取得します。ファイル名を取得できるメソッドは2種類あります。getLongFilenameはファイル名をそのまま取得でき、getFilenameはファイル名を短縮して取得できます。*3
ここまでやったら最後はFileOutputStream#writeにgetDataで取得した添付ファイルのバイト配列を渡してやればOK!