Does anyone know how to change the disk_service.rb to specify upload paths, for example:
my_path = Rails.root.join("public", "websites", "example.com", "users", current_user.id, "avatar")
current_user.avatar.attach(file, my_path)
This would result in having the file uploaded here:
/public/websites/example.com/users/12345/avatar/blah.png
And then I'd be able to:
rails_representation_path( current_user.avatar.variant(resize: "100x100"), disposition: 'attachment')
and get back the path:
/websites/example.com/users/12345/avatar/blah-100x100.png
This will allow getting rid of many issues around the ActiveStorage public/private URLs and related to CDN caching.
I am playing with https://github.com/rails/rails/blob/master/activestorage/lib/active_storage/service/disk_service.rb but can't figure out much of what it does and how it works really.
In your config/storage.yml you should have a configuration block for the local storage service, you can change where the attachments are stored by specifing a different path for root.
local:
service: Disk
root: <%= Rails.root.join('storage') %>
I don't think that you'll be able to address the specific images in the manner you are describing. ActiveRecord creates a unique key for everything that is attached to a model and renames the files with that key.
Related
I've been implementing an Active Storage Google strategy on Rails 5.2, at the moment I am able to upload files using the rails console without problems, the only thing I am missing is if there is a way to specify a directory inside a bucket. Right now I am uploading as follows
bk.file.attach(io: File.open(bk.source_dir.to_s), filename: "file.tar.gz", content_type: "application/x-tar")
The configuration on my storage.yml
google:
service: GCS
project: my-project
credentials: <%= Rails.root.join("config/myfile.json") %>
bucket: bucketname
But in my bucket there are different directories such as bucketname/department1 and such. I been through the documentation and have not found a way to specify further directories and all my uploads end up in bucket name.
Sorry, I’m afraid Active Storage doesn’t support that. You’re intended to configure Active Storage with a bucket it can use exclusively.
Maybe you can try metaprogramming, something like this:
Create config/initializers/active_storage_service.rb to add set_bucket method to ActiveStorage::Service
module Methods
def set_bucket(bucket_name)
# update config bucket
config[:bucket] = bucket_name
# update current bucket
#bucket = client.bucket(bucket_name, skip_lookup: true)
end
end
ActiveStorage::Service.class_eval { include Methods }
Update your bucket before uploading or downloading files
ActiveStorage::Blob.service.set_bucket "my_bucket_name"
bk.file.attach(io: File.open(bk.source_dir.to_s), filename: "file.tar.gz", content_type: "application/x-tar")
I have a field on my model Site called file_link. In the edit form, I want there to be a field for file_link with a Browse button next to the field, which pulls up a file browser on their local computer. I want them to be able to select a file, then have rails save the users local Path to the file, not the actual file.
For Example: file_link should save the path: N:\Projects\excelfile.xlsx
How can this be achieved?
when you visit a website, there is no way for the website to access your filesystem unless they somehow hack you. This isn't specific to Rails sites ... it's a fundemental security precaution of the internet. If you want to access your users' filesystem, you may be able able to do it with Javascript after gettin their permission, but might not be be easy. See Can javascript access a filesystem?
However, if you are building this app for localhost use only, you can use Ruby to manipulate/show the filesystem all you won't. But it's going to be limited to the filesystem running the Ruby program.
There few gems able to help you with that. And one of them is called Carriervawe.
Gem LINK
u = Site.new
u.file_link = params[:file] # Assign a file like this, or
# like this
File.open('somewhere') do |f|
u.file_link = f
end
u.save!
u.file_link.url # => '/url/to/file.png'
u.file_link.current_path # => 'path/to/file.png'
u.file_link_identifier # => 'file.png'
My app is on Heroku and I'm creating resources from the import of an Excel
Users of my app can import contacts from an Excel spreadsheet. The process of doing an import has multiple steps, to make it easier on the user's brain. In the first step, they upload a file (the spreadsheet). In the second step, they choose some options to process the file. These two steps can't be combined into one, because the options are dependent on the header of the file.
Right now, this is done as two actions: a POST to upload the file, and then another POST to upload the user's choices. The file, which is instanciate as a Tempfile, is not being persisted across the actions.
So, I don't want the user to upload the first file again in the intermediary action. Is this possible?
First step:
= simple_form_for [:choose_headers, #contact_import] do |f|
= f.input :file, as: :file
= f.submit
In this second step, the file is not persisted:
= simple_form_for [#contact_import] do |f|
= f.input :file, as: :file
= f.input :some_other_input
= f.submit
Don't use a Tempfile. Generate your own unique filename for the uploaded data, and save the file in a special directory. (If you are using Capistrano for deployment, I would put the upload directory under shared when deployed in production. In config/deploy.rb, I would add a deploy hook which creates the upload directory if it doesn't already exist. Then I would specify the relative path of the upload directory using a config value in config/environments/production.rb and config/environments/development.rb, so everything works seamlessly in both dev and production. In my controller actions, I would do something like File.join(Rails.root, UPLOAD_PATH, filename) to build the correct path for an uploaded file.)
Save the generated filename in your database, and retrieve it when the user comes back for the second step. Also, add a custom rake task which cleans up old uploads which were never used, and in production, run that task from a cron job. (I recommend using the whenever gem for configuring your cron jobs.)
I'm new to rails, and I'm writing a RESTful website using the CRUD technique. So far I have created three pages, all of which allow the user to create, edit, and delete a row from the database. However, my fourth page will need to include an upload file form, but a) I don't know how the filesystem works with Rails thus I don't know where files should be stored. The file would be around 100kb and couldn't be stored in temporary storage because it will be constantly downloaded. And b) I don't know how to write to a file.
It would be great if you could tell me how to do what I mentioned above - create an upload input on an input form, and to then write the file to a filepath in a separate directory.
Update 2018
While everything written below still holds true, Rails 5.2 now includes active_storage, which allows stuff like uploading directly to S3 (or other cloud storage services), image transformations, etc. You should check out the rails guide and decide for yourself what fits your needs.
While there are plenty of gems that solve file uploading pretty nicely (see https://www.ruby-toolbox.com/categories/rails_file_uploads for a list), rails has built-in helpers which make it easy to roll your own solution.
Use the file_field-form helper in your form, and rails handles the uploading for you:
<%= form_for #person do |f| %>
<%= f.file_field :picture %>
<% end %>
You will have access in the controller to the uploaded file as follows:
uploaded_io = params[:person][:picture]
File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'wb') do |file|
file.write(uploaded_io.read)
end
It depends on the complexity of what you want to achieve, but this is totally sufficient for easy file uploading/downloading tasks. This example is taken from the rails guides, you can go there for further information: http://guides.rubyonrails.org/form_helpers.html#uploading-files
Sept 2018
For anyone checking this question recently, Rails 5.2+ now has ActiveStorage by default & I highly recommend checking it out.
Since it is part of the core Rails 5.2+ now, it is very well integrated & has excellent capabilities out of the box (still all other well-known gems like Carrierwave, Shrine, paperclip,... are great but this one offers very good features that we can consider for any new Rails project)
Paperclip team deprecated the gem in favor of the Rails ActiveStorage.
Here is the github page for the ActiveStorage & plenty of resources are available everywhere
Also I found this video to be very helpful to understand the features of Activestorage
There is a nice gem especially for uploading files : carrierwave. If the wiki does not help , there is a nice RailsCast about the best way to use it . Summarizing , there is a field type file in Rails forms , which invokes the file upload dialog. You can use it , but the 'magic' is done by carrierwave gem .
I don't know what do you mean with "how to write to a file" , but I hope this is a nice start.
Okay. If you do not want to store the file in database and store in the application, like assets (custom folder), you can define non-db instance variable defined by attr_accessor: document and use form_for - f.file_field to get the file,
In controller,
#person = Person.new(person_params)
Here person_params return whitelisted params[:person] (define yourself)
Save file as,
dir = "#{Rails.root}/app/assets/custom_path"
FileUtils.mkdir(dir) unless File.directory? dir
document = #person.document.document_file_name # check document uploaded params
File.copy_stream(#font.document, "#{dir}/#{document}")
Note, Add this path in .gitignore & if you want to use this file again add this path asset_pathan of application by application.rb
Whenever form read file field, it get store in tmp folder, later you can store at your place, I gave example to store at assets
note: Storing files like this will increase the size of the application, better to store in the database using paperclip.
In your intiallizer/carrierwave.rb
if Rails.env.development? || Rails.env.test?
config.storage = :file
config.root = "#{Rails.root}/public"
if Rails.env.test?
CarrierWave.configure do |config|
config.storage = :file
config.enable_processing = false
end
end
end
use this to store in a file while running on local
I have a Rails application hosted on Heroku. The app generates and stores PDF files on Amazon S3. Users can download these files for viewing in their browser or to save on their computer.
The problem I am having is that although downloading of these files is possible via the S3 URL (like "https://s3.amazonaws.com/my-bucket/F4D8CESSDF.pdf"), it is obviously NOT a good way to do it. It is not desirable to expose to the user so much information about the backend, not to mention the security issues that rise.
Is it possible to have my app somehow retrieve the file data from S3 in a controller, then create a download stream for the user, so that the Amazon URL is not exposed?
You can create your s3 objects as private and generate temporary public urls for them with url_for method (aws-s3 gem). This way you don't stream files through your app servers, which is more scalable. It also allows putting session based authorization (e.g. devise in your app), tracking of download events, etc.
In order to do this, change direct links to s3 hosted files into links to controller/action which creates temporary url and redirects to it. Like this:
class HostedFilesController < ApplicationController
def show
s3_name = params[:id] # sanitize name here, restrict access to only some paths, etc
AWS::S3::Base.establish_connection!( ... )
url = AWS::S3::S3Object.url_for(s3_name, YOUR_BUCKET, :expires_in => 2.minutes)
redirect_to url
end
end
Hiding of amazon domain in download urls is usually done with DNS aliasing. You need to create CNAME record aliasing your subdomain, e.g. downloads.mydomain, to s3.amazonaws.com. Then you can specify :server option in AWS::S3::Base.establish_connection!(:server => "downloads.mydomain", ...) and S3 gem will use it for generating links.
Yes, this is possible - just fetch the remote file with Rails and either store it temporarily on your server or send it directly from the buffer. The problem with this is of course the fact that you need to fetch the file first before you can serve it to the user. See this thread for a discussion, their solution is something like this:
#environment.rb
require 'open-uri'
#controller
def index
data = open(params[:file])
send_data data, :filename => params[:name], ...
end
This issue is also somewhat related.
First you need create a CNAME in your domain, like explain here.
Second you need create a bucket with the same name that you put in CNAME.
And to finish you need add this configurations in your config/initializers/carrierwave.rb:
CarrierWave.configure do |config|
...
config.asset_host = 'http://bucket_name.your_domain.com'
config.fog_directory = 'bucket_name.your_domain.com'
...
end