Question

I have a downloads_controller.rb with a single download action which I want to trigger the download of a file that lives in a folder called downloads which lives in a folder called download_assets which I have added to my asset paths.

- download_assets [Added to asset paths]
   - downloads
      - file_1.pdf
      - file_2.pdf
      ...

I can successfully access any files in the folder using:

http://my-app.dev/assets/downloads/file.pdf

In order to use send_file I need the file system path to this file rather than the URL. I can get the path the the root of my Rails project using Rails.root, and the path to the file using asset_path(path). However, the problem is that because I'm in development, there is no file at this path. The file is stored in:

path/to/rails/project/app/assets/download_assets/downloads/file.pdf

The action:

   def download
      @download = Download.find(params[:id])
      file_path = "downloads/#{@download.filename}.pdf"
      file_path = "#{Rails.root}#{ActionController::Base.helpers.asset_path(file_path)}"
      send_file(file_path, :type=>"application/pdf", x_sendfile: true)
   end

To get this to work in development I need to use the following:

"#{Rails.root}/app/assets/download_assets/#{file_path}"

However this will fail in Production because assets will be precompiled and moved into assets.

My current workaround is:

   file_path = "downloads/#{@download.filename}.pdf"
   if Rails.env == "development" || Rails.env == "test"
        file_path = "#{Rails.root}/app/assets/download_assets/#{file_path}"
    else
        file_path = "#{Rails.root}{ActionController::Base.helpers.asset_path(file_path)}"
    end

Is there an alternative to supplying a different path based on environment as this seems fragile?

Was it helpful?

Solution

Yes.

In /config/initializers create a file called say "config.yml" Set it up like so:

config.yml:

---
## NOT a tab character.  3 spaces.  (In case that affects you)                                                                                                                            
development:
   path_to_uploads: /path/to/downloads/for/development

production:
   path_to_uploads: /path/to/downloads/for/production

test:
   path_to_uploads: /path/to/downloads/for/test

Then create a file in the same directory (/config/initializers/) called config.rb

config.rb:

APP_CONFIG = YAML.load_file("#{Rails.root}/config/initializers/config.yml")

Hop over to your controller:

foo_controller.rb:

      class FooController < ApplicationController
           def download
              # ... 
              path_to_uploads = Rails.root.to_s + APP_CONFIG["#{Rails.env}"]['path_to_uploads'] 
## By handing it the Rails.env object, it will return the current environment and handle selecting the correct environment for you.
        end
    end

There is an excellent RailsCast on using YAML to find the environment here.

Hope that helps!


Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top