質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.40%

  • Ruby on Rails

    9299questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

[Rails]ActionDispatch::Http::UploadedFileオブジェクトを表示・保存させる方法を知りたい

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 341

s_diff

score 97

知りたいこと

以下の3つがわからず開発が止まっています

  1. ActionDispatch::Http::UploadedFileを表示する方法
  2. ActionDispatch::Http::UploadedFileを保存する方法
  3. ビューを隔ててもzipファイルを保存する方法

環境

rails 4.2.6
jquery-ui-rails 6.0.1
mysql  Ver 8.0.11 for osx10.13 on x86_64 (Homebrew)

productsテーブルのカラムは以下です。

t.integer "area_id",          limit: 4
t.string   "building_name",    limit: 255
t.string   "number",           limit: 255
t.string   "station",          limit: 255
t.string   "address",          limit: 255
t.text     "zip",              limit: 65535
t.text     "products_image1",  limit: 65535
※ products_image1 ~ 30まであります。

実装手順

  1. pre_sort.html.erbで6つのカラムに入力し、その後ドラッグ&ドロップ(dropzone.js)でinputタグ(name=products_images[])に画像ファイルを複数入れて「作成」を押します。
    イメージ説明
  2. sort.html.erbでproducts_imagesを一覧表示させて、jQuery-UIのSortableで並び替えて「作成」を押し、保存します。
    イメージ説明

問題点

  1. sort.html.erbに遷移した時点で、products_imagesにはActionDispatch::Http::UploadedFileオブジェクトの配列が入っていましたが、個々をそのままimage_tagやdivタグのbackground-imageにいれても画像が表示されません。

  2. sort.html.erbでバリデーションを外して送信すると、products_image1~30とzipに値が入っていますが、何も表示されません。products_image1~30とzip両方共、バリデーションを記述するとレコード作成は弾かれます。

実装コード

ルーティング

#routes.rb
namespace :manage do
  resources :products do
    member do
      get 'pre_sort'
      post 'sort'
      post 'create_sorted'
    end
  end
end

コントローラ

#manage/products_controller.rb
  def pre_sort
    @product = Product.new
    @areas = Area.all
  end

  def sort
    @product = Product.new(product_params)
    @products_images = params[:products_images]
  end

  def create_sorted
    @product = Product.new(product_params)

    @product.building_name = ActiveSupport::Multibyte::Unicode.normalize(@product.building_name, :c)
    @product.address = ActiveSupport::Multibyte::Unicode.normalize(@product.address, :c)

    num = 1
    params[:products_images].each do |img|
      @product.send("products_image#{num}=", img)
      num += 1
    end

    respond_to do |format|
      if @product.save
        format.html { redirect_to [:manage, @product], notice: "#{@product.building_name}を作成しました。" }
        format.json { render :show, status: :created, location: @product }
      else
        format.html { render :pre_sort }
        format.json { render json: @product.errors, status: :unprocessable_entity }
      end
    end
  end

ビュー①(pre_sort.html.erb)

#manage/products/pre_sort.html.erb

<%= form_for([:manage,@product], url: { action: "sort"}, html: { multipart: true }) do |f| %>
  <div class="input manage-product-create-form">
    <p class="row"><%= f.label :area_id, class: "label" %><%= f.select :area_id, @areas.map{ |t| [t.name, t.id] }, include_blank: true %></p>
    <p class="row"><%= f.label :building_name, class: "label" %><%= f.text_field :building_name %></p>
    <p class="row"><%= f.label :number, class: "label" %><%= f.text_field :number %></p>
    <p class="row"><%= f.label :floor, class: "label" %><%= f.select :floor, Request::FLOOR, include_blank: true %></p>
    <p class="row"><%= f.label :station, class: "label" %><%= f.text_field :station %></p>
    <p class="row"><%= f.label :address, class: "label" %><%= f.text_field :address %></p>
    <p class="row" id="js-selectFile">
      <%= f.label "ダウンロードZIP", class: "label" %>
      <%= f.file_field :zip, id: "js-upload" %>
      <%= f.hidden_field :zip_cache, id: "js-upload-cache" %>
      <button class="original-btn">ZIPファイルを選択</button>
      <span class="icon">未選択</span>
    </p>
    <input type='file' name='products_images[]' multiple=true class='uploadFile' id="fileInput" />
    <div id="drop_zone"><i class="fa fa-picture-o" aria-hidden="true"></i> 物件写真をドラッグ&ドロップしてください</div>
    <div id="preview"></div>
  </div>
  <div class="actions">
    <%= button_tag type: 'submit', class: "btn-edit" do %>
      <i class="fa fa-pencil" aria-hidden="true"></i> 作成
    <% end %>
  </div>
<% end %>

ビュー②(sort.html.erb)

#manage/products/sort.html.erb
<div class="input manage-product-create-form">
  <p class="row"><label class="label">エリア</label><%= @product.area_id %></p>
  <p class="row"><label class="label">物件名</label><%= @product.building_name %></p>
  <p class="row"><label class="label">号室</label><%= @product.number %></p>
  <p class="row"><label class="label">間取り</label><%= @product.floor %></p>
  <p class="row"><label class="label">駅名</label><%= @product.station %></p>
  <p class="row"><label class="label">住所</label><%= @product.address %></p>
  <p class="row"><label class="label">DL用zip</label><%= @product.zip.url %></p>

  <% (@products_images).each do |img| %>
    <%= image_tag img, size: '100x100'  %>
  <% end %>

  <%= form_for([:manage,@product], url: { action: "create_sorted"}, html: { multipart: true }) do |f| %>
    <div class="file-upload-area">
      <div class="sort-box-container sortable">
        <% (@products_images).each do |img| %>
          <div class="sort-box">
            <input type="text" class="form-control" readonly="" />
            <div style='border: dashed 1px #ccc;'>
              <%= hidden_field_tag 'products_images[]', value: img, class: 'uploadFile', style: 'width: 90px; font-size: 16px;' %>
              <div class="imagePreview item" style='padding: 0; background-image: <%= img %>'></div>
            </div>
          </div>
        <% end %>
      </div>
    </div>
    <%= f.hidden_field :area_id, value: @product.area_id %>
    <%= f.hidden_field :building_name, value: @product.building_name %>
    <%= f.hidden_field :number, value: @product.number %>
    <%= f.hidden_field :floor, value: @product.floor %>
    <%= f.hidden_field :station, value: @product.station %>
    <%= f.hidden_field :address, value: @product.address %>
    <%= f.hidden_field :zip, value: @product.zip_url %>
  </div>

  <div class="actions">
    <%= button_tag type: 'submit', class: "btn-edit" do %>
      <i class="fa fa-pencil" aria-hidden="true"></i> 作成
    <% end %>
  </div>
<% end %>

Sortable.js

$(function() {
  $('.sortable').sortable({
    update: function(e, ui) {
      $('.sortable').sortable('toArray');
    }
  });
  $(document).on('change', '.uploadFile', function() {
    var input = $(this);

    label = input.val().replace(/\_/g, '').replace(/\.[^/.].+$/, "").slice( -12 );
    input.parent('div').prev('.form-control').val(label);

    var files = !!this.files ? this.files : [];
    if (!files.length || !window.FileReader) return;
    if (/^image/.test( files[0].type)){
      var reader = new FileReader();
      reader.readAsDataURL(files[0]);
      reader.onloadend = function(){
        input.next('.imagePreview').css("background-image", "url("+this.result+")");
      }
    }
  });
});


Dropzone.js

$(function(){
  var formData = new FormData();
  var dropZone = document.getElementById("drop_zone");
  var fileInput = document.getElementById("fileInput");

  if (!($('#drop_zone').length)){
    return false;
  }

  dropZone.addEventListener("dragover", function(e) {
    e.stopPropagation();
    e.preventDefault();

    this.style.background = "#87cefa";
  }, false);

  dropZone.addEventListener("dragleave", function(e) {
    e.stopPropagation();
    e.preventDefault();

    this.style.background = "#ffffff";
  }, false);

  dropZone.addEventListener("drop", function(e) {
    e.stopPropagation();
    e.preventDefault();

    this.style.background = "#ffffff";

    var files = e.dataTransfer.files;

    fileInput.files = files;
    for (var i = 0; i < files.length; i++) {
      (function() {
        var fr = new FileReader();
        fr.onload = function() {
          var div = document.createElement('div');

          var img = document.createElement('img');
          img.setAttribute('src', fr.result);
          div.appendChild(img);

          var preview = document.getElementById("preview");
          preview.appendChild(div);
        };
        fr.readAsDataURL(files[i]);
      })();

      formData.append("file", files[i]);
    }
  }, false);

});

長くなってしまい申し訳ありません。
ご指摘いただけることがあれば何卒よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

0

インスタンスをそのまま出力しても画像になるわけはないですが、なにぶん僕も経験が浅いのでActionDispatch::Http::UploadedFileについてはさほど詳しくないです。
ただActionDispatch::Http::UploadedFile#readを使ってバイナリデータを取得してそれを画像ファイルに書き出してパス指定してViewに表示って流れが妥当なんですかね?
保存もインスタンスをそのまま保存しようとするのではなくて、要素を取り出して保存すればいいのではないでしょうか?

ソースコード

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/15 15:31 編集

    うーん、これはインスタンスをそのまま使っているってわけではないと思います。
    ドロップされたファイルのDataTransferオブジェクトを取得して
    さらにループの中でごにょごにょと加工して表示しているように見えます。

    キャンセル

  • 2019/02/15 15:34 編集

    別の箇所のことかもしれませんが
    それは本当にActionDispatch::Http::UploadedFileインスタンスでしたか?
    インスタンスをそのまま出力して画像になるのはちょっと想像がつかないです...。

    キャンセル

  • 2019/02/15 15:42

    「インスタンスをそのまま」の意味を取り違えていみたいですね。
    申し訳ありません。
    ん〜たしかそうだったはずなのですが。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.40%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Ruby on Rails

    9299questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。