Show Rails Carrierwave URL without exposing entire path

早过忘川 提交于 2020-01-02 10:33:17

问题


I'm making a webapp with paid downloads, and I followed Carrierwave's guide for protecting uploads. Everything is working as expecting save one thing. Here's my current setup:


Product File Uploader

...
def store_dir
    "#{Rails.root}/downloads/#{model.product_id}/#{model.id}"
end
...

Routes

  get "/downloads/:product_id/:product_file_id/:filename", :controller => "products", action: "download", conditions: {method: :get}

ProductsController

def download
  product_file = ProductFile.find(params[:product_file_id])
  name = File.basename(product_file.file.file.file)
  send_file "#{Rails.root}/downloads/#{product_file.product.id}/#{product_file.id}/#{name}", :x_sendfile => true
end

The problem is with the download path. Because I specified #{Rails.root} in the File Uploader, the file saves in a folder external from the public folder called downloads. This has an unintended side effect, however. When I retrieve the URL from my model, I get the entire absolute path, not the path relative to the Rails Root.

So for instance, ProductFile.first.file.url returns "/home/doomy/Documents/resamplr/downloads/3/1/aaaa.zip", when it should instead just return /downloads/3/1/aaa.zip.

This behaviour seems to be attached to what is specified in the ProductFileUploader.

For instance, if I change the store_dir to "/downloads/#{model.product_id}/#{model.id}", I get an access error as it is trying to upload to the root of my PC. "downloads/#{model.product_id}/#{model.id}" simply uploads to a new folder in the public directory called downloads, which is not what I want to do.

Any advice on how to proceed? Thanks.


Edit:

I have found that setting the config.root in a Carrierwave config file to the Rails.root allows for this behavior. If I do this, in my ProductFileUploader I can specify the store_dir as "downloads/#{model.product_id}/#{model.id}.

However, this means public files are saved with a "/public/..." link. Rails automatically discards the public foldername and uses subdirectory names in the URL.


回答1:


Found out how to solve this!

In each uploader, there is a special method called root that we can use to set the unseen filepath. First, I made a special uploader base class called PublicUploader.

class PublicUploader < CarrierWave::Uploader::Base
  def root
    "${Rails.root}/public"
  end
end

Then, I set the global CarrierWave root to the Rails.root.

CarrierWave.configure do |config|
  # These permissions will make dir and files available only to the user running
  # the servers
  config.permissions = 0600
  config.directory_permissions = 0700
  config.storage = :file

  # This avoids uploaded files from saving to public/ and so
  # they will not be available for public (non-authenticated) downloading
  config.root = Rails.root
end

This means in my private file uploader I can simply write

def store_dir
  "downloads/#{model.product_id}/#{model.id}"
end

and it will save in "#{Rails.root}/downloads/..." as opposed to "#{Rails.root}/public/downloads/...". This means I can use a controller to limit access.

However, in my public uploader, I simply had...

def store_dir
  "public/downloads/#{model.product_id}/#{model.id}"
end

...because I wanted to save in my "#{Rails.root}/public" folder. However, this meant that rails would show a url like the following

/public/downloads/myfile.jpg

This poses a problem, as Rails omits the public from the path. So the above would 404. However when the uploader inherits from the special class where I define the root, then I can simply say

def store_dir
  "downloads/#{model.product_id}/#{model.id}"
end

It will upload and show the image correctly! Hope this can help someone.



来源:https://stackoverflow.com/questions/41337412/show-rails-carrierwave-url-without-exposing-entire-path

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!