sandre89
sandre89

Reputation: 5908

Rails ActiveStorage returns 404 even tough attachment file exists

I'm having 404's trying to use ActiveStorage. Here's the code:

class Model
  has_many_attached :attachments, dependent: :destroy

# In form
<%= form.file_field :attachments, multiple: true %>

# In controller
def model_request_params
  params.require(:model_name).permit(:title, attachments: [])
end

# In config/storage.yml
local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

# In development.rb
config.active_storage.service = :local

# In view
<%#= image_tag @model_instance.attachments.first %>

When I open the browser, the generated HTML is like this: http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBDZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--3814080e8c7e26964c927840a18034d727c61d87/file.jpg, but this returns a 404 not found.

The weird thing is that I have a different Rails 5.2.0 project and as soon as this GET is fired, the server console shows Precessing by ActiveStorage::Blobs Controller.

On this project however, I only see this:

Started GET "/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBDZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--3814080e8c7e26964c927840a18034d727c61d87/file.jpg" for 127.0.0.1 at 2018-07-23 12:15:36 -0300

It is NOT followed by anything else, not indicating any controller or whatever, but the browser gets a 404 and the image is never loaded.

I can confirm the attachment is uploaded, because not only model_instance.attachments[0] returns a ActiveStorage::Attachment, but the file is also present in my project's storage/ folder.

System configuration

Rails version: 5.2.0 (not brand new, updated - I don't see this bug in a brand new Rails 5.2.0 app)

Ruby version: 2.3.6

Upvotes: 4

Views: 2531

Answers (2)

sorenwiz
sorenwiz

Reputation: 63

My problem also seemed to be that my queue adapter wasn't running in development, so the images could not be processed config.active_job.queue_adapter = :inline in development.rb fixed that for me

Upvotes: 0

sandre89
sandre89

Reputation: 5908

Here's the culprit, I had this at the very beginning of my routes.rb file:

if Rails.env.development?
    scope format: true, constraints: { format: /jpg|png|gif|PNG/ } do
      get '/*anything', to: proc { [404, {}, ['']] }
    end
  end

I had totally forgotten about this hack. Here's some context:

We frequently dump the production database to our dev machines to have an up-to-date dev environment. But we do NOT copy the entire public/uploads folder because it's crazy huge.

This makes so that many of the pages that link to uploaded assets are VERY slow to load, because each image hits the dev application server which errors out with 404.

After this slowing us down for years, we found this excellent solution in a makandra card:

When you load a dump for development, records may reference images that are not available on your machine. Requests to those images may end up on your application, e.g. if a catch-all route is defined that leads to a controller doing some heavy lifting. On pages with lots of missing images, this slows down development response times. You can fix that by defining a Rails route like this: (code above)

Images are (usually) served directly from public or assets and won't hit your controllers/routes, as long as the files exist. If files are missing, the request will be handled by the above route which instantly responds with an empty HTTP 404 response.

Needless to say, that hacky fix was hijacking the /rails routes used by ActiveStorage because they satisfied the constraint.

I can't comment on makandra cards, but I hope Google will bring people here.

In the meantime, I changed the hack to this:

if Rails.env.development?
    scope format: true, constraints: { format: /jpg|png|gif|PNG/ } do
      get '/*anything', to: proc { [404, {}, ['']] }, constraints: lambda { |request| !request.path_parameters[:anything].start_with?('rails/') }
    end
  end

Upvotes: 4

Related Questions