DebakeDSP94
DebakeDSP94

Reputation: 65

Actiontext image upload fails in Rails 7

I am attempting to use the trix/actiontext feature of rails 7 to upload images to be included in my blog. I am planning to store them in AWS-S3 bucket, but the problem also happens when trying to store on local disk. Trix works normally when just doing text content with no errors. When I click the "paperclip" icon to attach a file, I get prompted to select a file, and it appears that I am able to, however, there are errors in the console and when I click submit, the file does not save. This is the error in the console as soon as I choose an image to attach:

POST http://localhost:3000/rails/active_storage/direct_uploads 422 (Unprocessable Entity) actiontext.js:543

Uncaught Error: Direct upload failed: Error creating Blob for "Screenshot from 2022-10-13 12-38-57.png". Status: 422 at AttachmentUpload.directUploadDidComplete (actiontext.js:849:13) at BlobRecord2.callback (actiontext.js:618:13) at BlobRecord2.requestDidError (actiontext.js:560:12) at BlobRecord2.requestDidLoad (actiontext.js:556:14) at XMLHttpRequest. (actiontext.js:527:56)

Here is the error from the rails server:

Started POST "/rails/active_storage/direct_uploads" for ::1 at 2022-11-03 14:20:10 -0500 14:20:10 web.1 | Processing by ActiveStorage::DirectUploadsController#create as JSON 14:20:10 web.1 | Parameters: {"blob"=>{"filename"=>"Screenshot from 2022-10-26 15-49-39.png", "content_type"=>"image/png", "byte_size"=>336871, "checksum"=>"TZneH7Z7DdCSftEvona6zg=="}, "direct_upload"=>{"blob"=>{"filename"=>"Screenshot from 2022-10-26 15-49-39.png", "content_type"=>"image/png", "byte_size"=>336871, "checksum"=>"TZneH7Z7DdCSftEvona6zg=="}}} 14:20:10 web.1

Completed 422 Unprocessable Entity in 33ms (ActiveRecord: 8.4ms | Allocations: 14851)

ActiveRecord::RecordInvalid (Validation failed: Service name can't be blank): 14:20:10

There is of course a much longer stack trace, but that appears to be the important part. I'm happy to provide the rest if anyone wants it.

When I click submit on the form, I get my normal toast message saying blog is successfully updated, but the image file is not added to the post. I've been searching for the answer to this problem for 3 days and I'm starting to go around in circles. I know that service_name is a column in the actives_storage_blobs database table, but I don't know where it pulls service name from or how to get it there. I'm thinking it's from the service in storage.yml, and it is present there.

storage.yml:

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

I'm running out of things to try and search for this problem. I have disabled turbo for the blogs page as I had found a github discussion relating to that, but it hasn't helped. I have read through the guides for active storage and action text multiple times and have not found anything that helped. I would greatly appreciate any suggestions. Thank you.

In case it helps:

Here is the relevant part of the form at app/views/blogs/_form.html.erb:

  <div class="form-group">
    <%= form.rich_text_area :body, class: 'form-control', rows: 15, placeholder: 'Content' %>
  </div>

app/controllers/blogs_controller.rb:

class BlogsController < CommentsController
  before_action :set_blog, only: %i[ show edit update destroy toggle_status ]
  before_action :set_sidebar_topics, except: [:update, :create, :destroy, :toggle_status]
  layout "blog"
  access all: [:show, :index], user: { except: [:destroy, :new, :create, :update, :edit, :toggle_status] }, admin: :all, testing: { except: [:destroy, :create, :update]}

  def index
    if logged_in?(:admin) || logged_in?(:testing)
      @blogs = Blog.recent.with_rich_text_body_and_embeds.page(params[:page]).per(5)
    else
      @blogs = Blog.published.recent.with_rich_text_body_and_embeds.page(params[:page]).per(5)
    end
    @page_title = "My Portfolio Blog"
  end

  def show
    if logged_in?(:admin) || logged_in?(:testing) || @blog.published?
      @blog = Blog.includes(:comments).friendly.find(params[:id])
      @comment = Comment.new
      @page_title = @blog.title
      @seo_keywords = @blog.body
    else redirect_to blogs_path, notice: 'You are not authorized to access this page.'
    end
  end

  def new
    @blog = Blog.new
  end

  def edit
  end

  def create
    @blog = Blog.new(blog_params)

    respond_to do |format|
      if @blog.save
        format.html { redirect_to blog_url(@blog), success: "Blog was successfully created." }
      else
        flash[:danger] = "Blog must have title, body and topic."
        format.html { render :new, status: :unprocessable_entity }
      end
    end
  end

  def update
    respond_to do |format|
      if @blog.update(blog_params)
        format.html { redirect_to blog_url(@blog), success: "Blog was successfully updated." }
      else
        format.html { render :edit, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @blog.destroy

    respond_to do |format|
      format.html { redirect_to blogs_url, status: :see_other }
    end
  end

  def toggle_status
    if @blog.draft? && logged_in?(:admin)
      @blog.published!
    elsif @blog.published? && logged_in?(:admin)
      @blog.draft!
    end
    redirect_to blogs_url, success: 'Post status has been updated.'
  end

  private

  def set_blog
    @blog = Blog.friendly.find(params[:id])
  end

  def blog_params
    params.require(:blog).permit(:title, :body, :topic_id, :status, images: [])
  end

  def set_sidebar_topics
    @side_bar_topics = Topic.with_blogs
  end
end

app/models/blog.rb:

class Blog < ApplicationRecord
  enum status: { draft: 0, published: 1 }
  extend FriendlyId
  friendly_id :title, use: :slugged

  validates_presence_of :title, :body, :topic_id

  has_rich_text :body
  has_many_attached :images, dependent: :destroy

  has_many :comments, as: :commentable, dependent: :destroy, counter_cache: :commentable_count

  belongs_to :topic, optional: true

  def self.special_blogs
    all
  end

  def self.featured_blogs
    limit(2)
  end

  def self.recent
    order("updated_at DESC")
  end
end
ActiveRecord::Schema[7.0].define(version: 2022_10_14_225319) do
  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"

  create_table "action_text_rich_texts", force: :cascade do |t|
    t.string "name", null: false
    t.text "body"
    t.string "record_type", null: false
    t.bigint "record_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true
  end

  create_table "active_storage_attachments", force: :cascade do |t|
    t.string "name", null: false
    t.string "record_type", null: false
    t.bigint "record_id", null: false
    t.bigint "blob_id", null: false
    t.datetime "created_at", null: false
    t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
    t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
  end

  create_table "active_storage_blobs", force: :cascade do |t|
    t.string "key", null: false
    t.string "filename", null: false
    t.string "content_type"
    t.text "metadata"
    t.string "service_name", null: false
    t.bigint "byte_size", null: false
    t.string "checksum"
    t.datetime "created_at", null: false
    t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
  end

  create_table "active_storage_variant_records", force: :cascade do |t|
    t.bigint "blob_id", null: false
    t.string "variation_digest", null: false
    t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
  end

  create_table "blogs", force: :cascade do |t|
    t.string "title"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "slug"
    t.integer "status", default: 0
    t.bigint "topic_id"
    t.integer "commentable_count"
    t.text "body"
    t.index ["slug"], name: "index_blogs_on_slug", unique: true
    t.index ["topic_id"], name: "index_blogs_on_topic_id"
  end

  create_table "comments", force: :cascade do |t|
    t.bigint "user_id", null: false
    t.string "commentable_type", null: false
    t.bigint "commentable_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.text "body"
    t.index ["commentable_type", "commentable_id"], name: "index_comments_on_commentable"
    t.index ["user_id"], name: "index_comments_on_user_id"
  end

  create_table "friendly_id_slugs", force: :cascade do |t|
    t.string "slug", null: false
    t.integer "sluggable_id", null: false
    t.string "sluggable_type", limit: 50
    t.string "scope"
    t.datetime "created_at"
    t.index ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true
    t.index ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type"
    t.index ["sluggable_type", "sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_type_and_sluggable_id"
  end

  create_table "portfolios", force: :cascade do |t|
    t.string "title"
    t.string "subtitle"
    t.text "body"
    t.text "main_image"
    t.text "thumb_image"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "position"
  end

  create_table "skills", force: :cascade do |t|
    t.string "title"
    t.integer "percent_utilized"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.text "badge"
  end

  create_table "technologies", force: :cascade do |t|
    t.string "name"
    t.bigint "portfolio_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["portfolio_id"], name: "index_technologies_on_portfolio_id"
  end

  create_table "topics", force: :cascade do |t|
    t.string "title"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "users", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "name"
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer "sign_in_count", default: 0, null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string "current_sign_in_ip"
    t.string "last_sign_in_ip"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "roles"
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end

  add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
  add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
  add_foreign_key "blogs", "topics"
  add_foreign_key "comments", "users"
  add_foreign_key "technologies", "portfolios"
end

Upvotes: 1

Views: 1432

Answers (1)

DebakeDSP94
DebakeDSP94

Reputation: 65

I am going to stop this question. It appears that rails needed updating. I did rails app:update and there were updates related to actiontext and activestorage and the DB schema was updated for those 3 relevant tables. I have done an upload through action text and it has worked. It's not displaying properly but there is data in my storage folder. I am going to go through everything as it is now to see if I can get the image to display properly and will ask a new question if needed. Thank you very much for helping.

Edit to add: Updating rails did fix the uploads completely. The update changed the image processor from mini-magick to vips. Adding this line to application.rb switched it back and then everything worked properly.

config.active_storage.variant_processor = :mini_magick

Upvotes: 5

Related Questions