TMailの添付ファイルの名前の取り出しとActionMailerの添付ファイルの名前付け

MIME云々を読み,rdocを読み,やっとできたorz
サンプルに使ったメールは,http://www.mew.org/Newsletters/3.htmlの一番下のマルチパートメール.コピペしてmultipart_mail.txtって名前で保存して,色々検証してみました.

TMailの添付ファイルの名前取り出し

はじめは,http://i.loveruby.net/ja/projects/tmail/doc/mail.htmlに書いてあった,

# example
mail.disposition_param('filename')

でできるか試してみたものの,まぁ結果できなかった(nilが返ってきた)わけで.


何とかしてこのcontent-dispositionを参照できないものかとウロウロしてて,何気にrdocのActionMailerのTMail::Mail#has_attachment?がどうなっているのか気になったので見てみると,

def has_attachments?
  multipart? && parts.any? { |part| attachment?(part) }
end

ってなってたわけです.次にこのTMail::Mail#attachment?を見る.

def attachment?(part)
  (part['content-disposition'] &&
  part['content-disposition'].disposition == "attachment") ||
  part.header['content-type'].main_type != "text"
end

ってなっていた.partの値って,ハッシュで参照できるみたい.結局,ファイル名を取り出す作業は,こうすれば良い.

require 'tmail'

mail = TMail::Mail.load('multipart_mail.txt')
mail.parts.each do |part|
  if part['content-disposition'] # えせActionMailer版TMail::Mail#attachment?
    puts part['content-disposition']['filename']
  end
end

こうすればTMailでの添付ファイルの名前の取り出しは,辛うじてできる:p


ところで,この辺の処理は,ActionMailerで言うとこのattachmentの名前の取り出し("の"が多い)には全くと言っていいほど関係がなかったりする.何故なら,partとattachmentが違うから.

ActionMailerにおける添付ファイルの名前の取り出し

partとattachment比較.

require 'rubygems'
require 'action_mailer'

email = TMail::Mail.load('multipart_mail.txt')

# ここからirbと考えてください

> email.parts # part
=> [#<TMail::Mail port=#<TMail::StringPort:id=0x105d92a> bodyport=nil>,
  #<TMail::Mail port=#<TMail::StringPort:id=0x105d718>
  bodyport=#<TMail::StringPort:id=0x105bd0a>>]
  
> email.attachments # attachment
=> [#<TMail::Attachment:0x20b753c>]

と,portがTMail::Mailクラスなのに対し,attachmentはTMail::Attachmentクラスである事がわかります.そこでrdocのaction_mailer,TMail::Attachmentを見てみると,Attributesに

  • content_type
  • originail_filename

とあります.このoriginal_filenameさえあれば,もう添付ファイルの名前問題は解決.

> email.attachments[0].original_filename
=> "wash.gif"

と,楽々参照できます.だから,Ride on Railsや色んな"ActionMailerいろは"に載っていた,「受信して添付ファイルを取り出しDBに格納」は,こうすると良いかも(動作確認はしてません).

class Mailman < ActionMailer::Base
  …
  def recieve(email)
    if email.has_attachments?
      email.attachments.each do |attachment|
        Attachment.create(
          :file => attachment.read
          :filename => attachment.original_filename
          :content_type => attachment.content_type
          # 他にも,添付されてたメールに関する情報を格納する
        )
      end
    end
  end
end

Content-Typeがimageならimgタグでブラウザに表示したり,textならpreタグで表示したりできるかも.docやpdf(そんなContent-Typeがあるのか?)ならダウンロード出来るようにしてみたり….結構色々できそうです.