LoginSignup
132
120

More than 5 years have passed since last update.

ActionDispatch::Http::UploadedFileを読む (デバッグメモ)

Last updated at Posted at 2013-05-20

Ruby on Rail の勉強用に、画像アップロードのwebアプリを作りました。
その際に詰まった部分 ActionDispatch::Http::UploadedFile についてメモ。

結論

ActionDispatch::Http::UploadedFile
Httpにて、画像データをuploadする際のライブラリ。

画像をwebとやりとりする形式
@original_filename : fileの名前 (UTF-8に強制的にエンコードされる)
@header: ヘッダー (height, width, サンプル精度など)
@tempfile : 画像データのリファレンス?
@content_type: 画像の形式 (jpgなど)

ruby on railsのサーバーで画像をbinaryデータとして保存する場合、
@tempfile.read メソッドを使ってバイナリデータを取得する必要がある。

画像データアップロードページの作成

手順1. scaffoldで簡単に土台を作成
rails generate scaffold post comment:string image:binary

手順2. 画像ファイルをアップロード出来るようにviewを書き換え (デフォルトではtext_fieldになっている。)

app/views/posts/_form.html.erb
<%= f.file_field :picture_file %>

アップロードされるデータの確認

とりあえずここまでで、どのようなデータがアップロードされているかを確認してみる。適当な画像をアップロードしてみると、次のようなエラー。

# POST /posts.json
def create
@post = Post.new(post_params) ←エラー部分

undefined method `encoding' for #
ActionDispatch::Http::UploadedFile:0x007f9f74e26f38

post_paramsの定義は以下のようになっている。

posts_controller.rb
    def post_params
      params.require(:post).permit(:comment,:image, :delete_time,:longitude, :latitude,:user_id)
    end

また、送られているparameterも以下のようになっている。

{"utf8"=>"✓",
"authenticity_token"=>"Nqdh3xtNz+Xn7C1u/HwE1dMvSpDOSXHb89M5W4m2xPQ=",
"post"=>{"comment"=>"debug_test",
"image"=>#@tempfile=#Tempfile:/var/folders/yy/k_9r50m10v11nhs4_ymy7jqm0000gn/T/RackMultipart20130520-783-ann4je,
@original_filename="350px-Japanese-Akita-Inu.jpg",
@content_type="image/jpeg",
@headers="Content-Disposition: form-data; name=\"post[image]\"; >filename=\"350px-Japanese-Akita-Inu.jpg\"\r\nContent-Type: image/jpeg\r\n">},
"commit"=>"Create Post"} `

jpg画像の読み込みフォーマットに問題がありそう。
そこでまず、ActionDispatch::Http::UploadedFileについて検索。
githubにて read me 発見.

Action Dispatchは webリクエスト/ユーザーによってルーティングされた情報を解析する。そして、HTTPと関連する MIME-type のようなネゴシエーションを行い、POST/PUTのbodyをデコードする。HTTPのキャッシュを扱うロジックはcookiesやsessionである。だそうだ。

詳細は全く記述されていなかったため、ソースコードを読んでみる。

upload.rb
module ActionDispatch
  module Http
    class UploadedFile
      attr_accessor :original_filename, :content_type, :tempfile, :headers

      def initialize(hash) #
        @original_filename = encode_filename(hash[:filename]) 
        @content_type      = hash[:type]     #画像の保存形式
        @headers           = hash[:head]       # header 情報
        @tempfile          = hash[:tempfile]  # 画像データのリファレンス?
        raise(ArgumentError, ':tempfile is required') unless @tempfile
      end

      def read(*args)
        @tempfile.read(*args)
      end

      # Delegate these methods to the tempfile.
      # つまり、tempfileについて、size, path, open, などなどが使えるということ
      [:open, :path, :rewind, :size].each do |method|
        class_eval "def #{method}; @tempfile.#{method}; end"
      end

      private
      #画像のファイルネームを取り込む部分
      #強制的にUTF-8に変換
      def encode_filename(filename) 
        # Encode the filename in the utf8 encoding, unless it is nil or we're in 1.8
        if "ruby".encoding_aware? && filename
          filename.force_encoding("UTF-8").encode!
        else
          filename
        end
      end
    end

    module Upload
      # Convert nested Hash to HashWithIndifferentAccess and replace
      # file upload hash with UploadedFile objects
      def normalize_parameters(value)
        if Hash === value && value.has_key?(:tempfile)
          UploadedFile.new(value)
        else
          super
        end
      end
      private :normalize_parameters
    end
  end
end

おそらく、imageが binaryデータを求めているのに、jpgファイル全てを入力したことが
最初のバグの原因でしょう。imageにbinaryデータのみを与えて上げると、バグは修正されました。

132
120
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
132
120