[Rails 6] The save timing of active_strage images has been changed.

This time it seems that the timing of saving images has changed from Rails 6, so I checked it.

Check Rails version

$ rails -v
Rails 6.0.2.1

Install Active Strage

$ rails active_storage:install
$ rails db:migrate

Preparing to attach an image

I will post the final product.

Add preview to route.

route.rb


Rails.application.routes.draw do
  resources :users do
    collection do
      post :preview
      patch :preview
    end
  end
end

Allows the model to handle images with has_one_attached.

user.rb


class User < ApplicationRecord
  has_one_attached :image
end

Next is the controller. This time I want to add a preview function, so I added a preview action.

user_controller.rb


class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  # GET /users
  # GET /users.json
  def index
    @users = User.all
  end

  # GET /users/1
  # GET /users/1.json
  def show
  end

  # GET /users/new
  def new
    @user = User.new
  end

  # GET /users/1/edit
  def edit
  end
.
.
.
.
  def preview
    @user = User.new(user_params)
    image_binary = ''
    if @user.image.attached?
      image_binary = @user.attachment_changes['image'].attachable.read
    else
      saved_user = User.find_by(id: params[:id])
      if saved_user.present? && saved_user.image.attached?
        image_binary = saved_user.image.blob.download
      end
    end
    @image_enconded_by_base64 = Base64.strict_encode64(image_binary)

    render template: 'users/_preview', layout: 'application'
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def user_params
      params.require(:user).permit(:name, :image)
    end
end

Finally, html.

ruby:new.html.erb



<h1>New User</h1>

<%= render 'form', user: @user %>

<%= link_to 'Back', users_path %>

ruby:_form.html.erb



<%= form_with(model: user, local: true) do |form| %>
  <% if user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
        <% user.errors.full_messages.each do |message| %>
          <li><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :name %>
    <%= form.text_field :name %>
    <%= form.label :image %>
    <%= form.file_field :image %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

ruby:show.html.erb



<p id="notice"><%= notice %></p>

<p>
  <strong>Name:</strong>
  <%= @user.name %>
  <% if @user.image.attached? %>
    <%= image_tag @user.image %>
  <% end %>
</p>

<%= link_to 'Edit', edit_user_path(@user) %> |
<%= link_to 'Back', users_path %>

ruby:_preview.html.erb


<p id="notice"><%= notice %></p>

<p>
  <strong>Name:</strong>
  <%= @user.name %>
  <% if action_name == 'preview' %>
    <img src="data:image/png;base64,<%= @image_enconded_by_base64 %>" />
  <% else %>
    <%= image_tag @member.image %>
  <% end %>
</p>

Now you are ready.

Timing of saving images in Rails 6

I will upload the image immediately.

スクリーンショット 2020-05-21 18.04.06.png

When you upload the test .jpeg and press the Create User button

スクリーンショット 2020-05-21 18.18.57.png

create is successful. If you go to see the image uploaded here

irb(main):005:0> @user.image.attachment
  ActiveStorage::Attachment Load (1.5ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 1], ["record_type", "User"], ["name", "image"], ["LIMIT", 1]]
=> #<ActiveStorage::Attachment id: 1, name: "image", record_type: "User", record_id: 1, blob_id: 1, created_at: "2020-05-21 09:04:12">

I found that the image exists.

Not saved by upload

Then try a new preview.

スクリーンショット 2020-05-21 18.20.20.png

In the same way, upload the preview .jpg and press the preview button to check the image.

Then

> @user.image.attachment
NameError: uninitialized constant #<Class:0x00007fd450065780>::Analyzable
from /usr/local/bundle/gems/activestorage-6.0.3.1/app/models/active_storage/blob.rb:26:in `<class:Blob>'

Absent! !!

> params[:user][:image]
=> #<ActionDispatch::Http::UploadedFile:0x000055892ec2b598
 @content_type="image/jpeg",
 @headers=
  "Content-Disposition: form-data; name=\"user[image]\"; filename=\"\xE3\x83\x95\xE3\x82\x9A\xE3\x83\xAC\xE3\x83\x92\xE3\x82\x99\xE3\x83\xA5\xE3\x83\xBC.jpg\"\r\nContent-Type: image/jpeg\r\n",
 @original_filename="preview.jpg ",
 @tempfile=#<File:/tmp/RackMultipart20200522-1-ha9tz1.jpg>>

Check the parameters. Isn't it saved just by uploading the image?

If you refer to the following https://github.com/rails/rails/pull/33303

on rails5

@user.image = params[:image]

It's the timing when you put it in the attribute!

No way there is such a change in rails 6! !! If this is left as it is, the image cannot be passed to View at the time of preview (without saving the image in the instance).

When I was looking for a good way

・ Https://github.com/rails/rails/pull/33303

・ Https://stackoverflow.com/questions/57564796/reading-from-active-storage-attachment-before-save

there were!!

record.attachment_changes['<attributename>'].attachable

try!

> @user.attachment_changes['image'].attachable
=> #<ActionDispatch::Http::UploadedFile:0x000055892ec2b598
 @content_type="image/jpeg",
 @headers=
  "Content-Disposition: form-data; name=\"user[image]\"; filename=\"\xE3\x83\x95\xE3\x82\x9A\xE3\x83\xAC\xE3\x83\x92\xE3\x82\x99\xE3\x83\xA5\xE3\x83\xBC.jpg\"\r\nContent-Type: image/jpeg\r\n",
 @original_filename="preview.jpg ",
 @tempfile=#<File:/tmp/RackMultipart20200522-1-ha9tz1.jpg>>

further

@user.attachment_changes['image'].attachable.read

I was able to fetch the binary data with ↑, so if I encode it with Base64 and pass it

@image_enconded_by_base64 = Base64.strict_encode64(@user.attachment_changes['image'].attachable.read)
スクリーンショット 2020-05-22 10.48.00.png

It's done! !!

Summary

I didn't know there was a change in the timing of saving Active Strage. If you want to touch the image without saving

record.attachment_changes['<attributename>'].attachable.read

Read the binary with Base64! !! (It doesn't have to be Base64)

Please let me know if there is any other good way.

Recommended Posts

[Rails 6] The save timing of active_strage images has been changed.
Has the content of useBodyEncodingForURI changed from Tomcat8?
The date time of java8 has been updated
[Rails] Save images using carrierwave
[Swift] Get the timing when the value of textField is changed
[Ruby on Rails] Change the save destination of gem refile * Note
[Rails] Check the contents of the object
Explanation of the order of rails routes
Check the migration status of rails
[Rails] How to solve the time lag of created_at after save method
Change the save destination of the image to S3 in the Rails app. Part 2
The identity of params [: id] in rails
part of the syntax of ruby ​​on rails
[Rails] Change the label name of f.label
The JacocoReportBase.setClassDirectories (FileCollection) method has been deprecated.
The process of introducing Vuetify to Rails
[Rails] We have summarized the storage locations and usage of developer and user images.