.msgファイルをパースして中から添付ファイルを抜き出す

[Java][Windows][Ruby][備忘録].msgファイルをパースして中から添付ファイルを抜き出す

正確にはRubyというには微妙なんですが…。

前回までのあらすじ

しかし、社内セキュリティGWの設定? のためかzipとかexeとか怪しい拡張子が付いている添付ファイルは一旦msgファイルに固められるという仕様のためそのままでは添付ファイルが見れない。

こういうメールが

メール
┗添付ファイル.zip

届いた時にはこうなっている

メール
┗ラップ.msg(\あやしいファイルだから注意してDLしてね/)
 ┗本来の添付ファイル.zip

なんとかRubyからmsgファイル内を解析して添付ファイルを引っこ抜いて保存ができまいか…というのが今回の問題。

結論

以下のライブラリを駆使して実現しました。

  • msgparser(Auxiliiという所のJava用msgファイルパースライブラリ)
  • jrb(Java Ruby Bridge: RubyからJavaVMを操作できるライブラリ)

一応Rubyでmsgファイルを解析するライブラリも探してみたのですが、あまりないのかな? http://en.sourceforge.jp/projects/sfnet_ssfiledll/downloads/SSFileDLL%20%28MAPI%29/1.0/SSFileDLL-MAPI-1.0-Source.zip/:title こういうdllは見つけたのですが、MAPIがよくわからん上に説明を見る限りHasAttach(添付ファイルを持ってるか持ってないか?)くらいしかわからない? ようなのであきらめました。

あと、msgparserはJavaライブラリなので当初はJRubyで書けば何とかなるかなと思ってました。簡単なサンプルは http://www.rgagnon.com/javadetails/java-0613.html:title このページのような感じ。が、今まで書いたコードをJRubyで実行すると失敗してしまったので方向転換;; RubyからJavaVMを操作できるrjbというライブラリを使う事に。

ダウンロード・インストール

gem install rjb

ソース

ソース全体は [https://github.com/gosyujin/outlook_for_ruby:title] 。msgParse.rbが本体、ライブラリがlibに入っています。

使い方としてはMsgParseをnewしてinputMsgで.msgファイルのパス指定、saveFileで添付ファイルぶっこぬいて出力先に保存としたい。こんな感じで。

    msg = MsgParse.new
    msg.inputMsg(MSGFILE)
    msg.saveFile(SAVEDIR)

という事でMsgParser[2]クラスを作成。ソースは [https://github.com/gosyujin/outlook_for_ruby/blob/master/msgParse.rb:title] 。まずは初期化から。

    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!

[1] complete package including source code, binaries and third party librariesと書いてあった

[2] ライブラリと同じ名前にしてしまった…。

[3] TODOとして、添付ファイル名に波ダッシュが入っているとエラーになるので要調査…。

関連記事(この記事の初版より古い記事はリンクがグレーで表示されます)

  1. 2011/09/07 [Ruby] [Redmine] [SQLite3] [Windows] Redmineのプラグイン作成のための備忘録と、時々SQLite3
  2. 2011/08/23 [Ruby] [Windows] MicrosoftOutlookで消せなくなったフォルダをwin32ole経由でRubyから消してみる
  3. 2011/07/09 [Windows] [Ruby] MicrosoftOutlookのメールをRuby(win32ole)で操作する! その2
  4. 2011/05/17 [Ruby] [Windows] MicrosoftOutlookのメールをRubyで操作する!
  5. 2011/05/01 [Ruby] [Redmine] [Windows] Redmineインストール備忘録(Windows)
  6. 2012/10/01 [Ruby] [Bundle] [Windows] bundle execを省略したいのでバッチを作った(Windows版)
  7. 2012/07/17 [Ruby] [Windows] [Redmine] Windows版Redmineをサービスに登録してブート時に起動させる(宿題あり)
  8. 2012/04/20 [Ruby] [Windows] ZenTestで実行したRSpecの結果をGrowlで通知してくれるようにした
  9. 2012/03/27 [Windows] [Jenkins] [Ruby] simplecovとsimplecov-rcovを使ってJenkinsでカバレッジを確認
  10. 2012/03/20 [Windows] [Jenkins] [Ruby] Windows環境用にrcovをビルドしなおす手順
  11. 2012/02/29 [Ruby] [Rails] [Windows] Rails3レシピブックを読みながらRailsを学ぶ モデル、コントローラ、ビュー、Railsの規約など
  12. 2012/02/28 [Ruby] [Windows] [Rails] Rails3レシピブックを読みながらRailsを学ぶ
  13. 2012/02/20 [Ruby] [Windows] Rubyの実行ファイルを作成するExerbとOcraを試してみた
  14. 2011/12/27 [Evernote] [Ruby] [API] EvernoteのAPIをRubyから叩きたい
  15. 2011/12/26 [Ruby] [Haml] [Sinatra] SinatraでHaml入門