Reading from Active Storage Attachment Before Save - ruby-on-rails

I have users uploading JSON files as part of a model called Preset, very standard Active Storage stuff. One thing that's somewhat out of the ordinary (I suppose, given my inability to make it work) is that I'd like to grab data from the uploaded JSON file and use it to annotate the Preset record, like so:
class Preset < ApplicationRecord
has_one_attached :hlx_file
before_save :set_name
def set_name
file = JSON.parse(hlx_file.download)
self.name = file['data']['name']
end
end
When I call hlx_file.download I get ActiveStorage::FileNotFoundError: ActiveStorage::FileNotFoundError.

Rails 6 changed the moment of uploading the file to storage to during the actual save of the record.
This means that a before_save or validation cannot access the file the regular way.
If you need to access the newly uploaded file you can get a file reference like this:
record.attachment_changes['<attributename>'].attachable
This will be a tempfile of the to-be-attached file.
NOTE: The above is an undocumented internal api and subject to change (https://github.com/rails/rails/pull/37005)

you use before_save :set_name which call the file before it actually being saved you can use after_save instead
try to use url_for() funtion file = JSON.parse(url_for(hlx_file))
also dont forget to include Rails.application.routes.url_helpers at your model

Related

Is there any way to save files created by Active Storege with custom file names in Rails?

I'm trying to create a form where users can upload videos by using active storage. But, when files are stored into Storage folder, by default they are using an ID as the file name. Is there any way, I can create files with a custom name like user's email as a file name. So, I can easily distinguish which files are uploaded by whom?
You shouldn't be concerned about the file name but do try this bit of code that should set the file name upon uploading your files:
class Model < ApplicationRecord
after_create :set_filename
def set_filename
if self.active_storage_object.attached?
self.active_storage_object.blob.update(filename: "desired_filename.#{self.active_storage_object.filename.extension}")
end
end
end
Source: https://medium.com/fiatinsight/how-to-change-a-filename-in-rails-active-storage-f3e4f26f427e
Read more about the AS storage model:
https://bloggie.io/#kinopyo/7-practical-tips-for-activestorage-on-rails-5-2
Note how the filename field shows up in the active_storage_blobs table.

CarrierWave use one uploader two times in single model

I have a model called User. It has two fields: small_logo and big_logo.
Those are actually different pictures, not just one resized picture.
class User < ActiveRecord::Base
...
mount_uploader :big_logo, UserLogoUploader
mount_uploader :small_logo, UserLogoUploader
...
end
I use UserLogoUploader to upload this pictures.
And I'm running onto a bug - as long as the name of the model is the same, uploaded files get the same route, so if I try to upload two different files with same names - second one overwrites first one.
The obvious solution is to use different uploaders for those fields. But I don't want to create another uploader just to fix this bug - is there anything I can do to modify filename, for example, with something meaningful like the name of a formfield that submitted this file or access name of a model field that is being processed.
Found an answer to my own question after some searching
There is a mounted_as attribute inside an uploader, which, reffering to docs, does exactly what I need:
If a model is given as the first parameter, it will stored in the uploader, and
available throught +#model+. Likewise, mounted_as stores the name of the column
where this instance of the uploader is mounted. These values can then be used inside
your uploader.
So the whole solution looks like this:
def UserLogoUploader < CarrierWave::Uploader::Base
...
def store_dir
File.join [
Settings.carrierwave.store_dir_prefix,
model.class.to_s.underscore,
mounted_as.to_s,
model.id.to_s
].select(&:present?)
end
...
end
This code creates different subfolders for different model fields, which helps preventing names duplication and files overwriting.

Rails 4 + Paperclip: How to rename file name in the database

I'm using Paperclip with Rails 4 to add attached video files to one of my models. I am able to name of the saved file after its new id like this:
has_attached_file :file, :url=>"/tmp/video_uploads/:id.:extension", :path=>":rails_root/tmp/video_uploads/:id.:extension"
This causes them to get saved to the right place, with the right name + original extension. However, when I look in the database, the file_file_name field for the new record is still the original file name (EX: scooby-dooby-doo.MOV). How do I fix this?
As far as I know, it's just an attribute:
object.file_file_name = 'something_else'
object.save
It seems to be there to retain the original file upload name. Changing that value doesn't really do anything.
edit: You say you're trying to make it easy to find the associated file, are you aware of the .url or .path methods on file?
object.file.path
object.file.url
Have a look at the attachment object on github.
In looking at that, it appears that re-assigning the value of file_file_name will "break" file.original_filename, in that it won't be accurate anymore. If you just want the file portion of the actual stored file, you could instead try something along these lines:
class MyModel < ActiveRecord::Base
has_attached_file :file
def actual_filename
File.basename(file.url)
end
end

Paperclip gem and custom filenames in database Rails 2.3

I'm trying to create a custom filename for files uploaded via the paperclip gem using Paperclip.interpoles in the initializer. The problem I'm having is this is updating the custom filename on the file system when the file is uploaded, but the database filename remains the name of the source file. Is there a better way then having to reassign the database attribute to handle this?
Try using before_create callback in ActiveRecord. As paperclip will not write the attached resource to disk until ActiveRecord::Base#save is called, this seems to be the right time to create your custom filename.
To do so, just register an ordinary method to create the custom filename. This will change the name of the attached image, which you'll then find on your file system and in your database.
Let's say you have a model, where you want to attach an image with a custom random filename.
In your model:
has_attached_file :image
before_create :randomize_image_file_name
Also in your model:
def randomize_image_file_name
extension = File.extname(image_file_name).downcase
self.image.instance_write(:file_name, "#{ActiveSupport::SecureRandom.hex(8)}#{extension}")
end
You can declare your method anywhere you want, though it's considered good practice to declare callback methods as protected or private.
This will save the attachment with a custom randomized filename.
Hope this helps.

rails active model to store data in an a file along with the database

I have a form and when its posted, some of its data gets saved into the database using the rails model. Along with the database, I need to store some of the content into a file. Can I enhance the "save" method in the model to write the content to the file? Is this a good design. If not what would be ideal design.
Continuing on this, I want to set the location where this file is stored, in the application configuration. Which file should I define this variable for the file location and how do I access it in the model/controller
Thanks
Kiran
You can of course use one of the existing, ready-made file-attachment libraries, such as paperclip and carrierwave.
Otherwise, you can:
# config/application.rb
# ...
config.my_app.cache_file_prefix = "/tmp/files"
# app/models/my_model.rb
class MyModel < ActiveRecord::Base
# Causes ActiveRecord to run this method
# before saving (creating or updating).
before_save :copy_to_file
private
def copy_to_file
# Write data to the file.
file_name = copy_to_file_name
File.open(file_name) do |f|
f.write("some data")
end
end
def copy_to_file_name
# Calculate and return the expected file name.
prefix = Rails.configuration.my_app.cache_file_prefix
"#{prefix}/#{id}"
end
end
Please note that this solution will not work once you have more than one server running your Rails application. You should consider using either an object storage provider (such as S3 or Rackspace) or a replicated or distributed file system (such as DRDB or GlusterFS).

Resources