This time it seems that the timing of saving images has changed from Rails 6, so I checked it.
$ rails -v
Rails 6.0.2.1
$ rails active_storage:install
$ rails db:migrate
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.
I will upload the image immediately.
When you upload the test .jpeg and press the Create User
button
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.
Then try a new preview.
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)
It's done! !!
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