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.
Related
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
With the release of Rails 5.2, the much used Paperclip gem is now deprecated and it's advised to use Active Storage that ships with Rails. I'm starting a new project and set up Active Storage with ease, but my problem comes when trying to add a name or description to the file uploads.
With Paperclip I would add a column to the model called something like file_upload_name, so that as well as having a file name "something.pdf" I could also add a name or description such as "My Important Document" on the upload form.
For the projects that I'm doing, this is a vital part of the upload process and ideally needs to be done at the time of upload. As Active Record doesn't store to a model in such a way it's not as simple as just adding a column and adding fields to a form. It seems something that should be relatively simple but I can't figure it out or find any information about how best to do it. Any help much appreciated.
Here's an example of what I'm trying to achieve:
With Active Storage the end result is a multiple file upload button, with no naming etc.
You should create a new model to wrap each attached file. That model would then have the ActiveStorage attachment defined on it, as well as whatever other attributes you need to capture. Ex:
class Attachment < ApplicationRecord
has_one_attached :file
end
Rails then treats file kind of like an attribute for each Attachment. You can define your other attributes (e.g. upload_name, etc.) on the Attachment model. Based on your screenshot, it looks like maybe a Quotation has many attached files, so you'd do something like:
class Quotation < ApplicationRecord
has_many :attachments
end
I have a Rails app where users can submit mp3 files to be uploaded. I want to set Ruby object attributes on them as they are uploaded. TagLib lets me grab metadata easily, and locally I can make that work fine, but I'm struggling to find out how to access the object before saving it to Amazon S3. Paperclip says that the objects are kept in memory until you .save them.
Where do attachments get stored before saving, and how do I access them? The path attribute on the model is the place where paperclip will store the file, future tense, on S3.
For reference, I'm trying to run a before_[attachment name]_post_process filter on my sound files to grab the length in seconds of the file and set that as an attribute.
I can't find this in the Paperclip docs (if you can, please let me know), but it turns out the name of the attachment as specified in your model class is the object. With TagLib, you access a sound's properties, before save, as follows:
# app/models/sound.rb
…code…
has_attached_file :soundfile
vv same name ^^
before_soundfile_post_process :set_song_attrs
…code…
def set_song_attrs
TagLib::FileRef.open(soundfile.queued_for_write[:original].path) do |file|
self.length = file.audio_properties.length
self.artist = file.tag.artist
end
end
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
Here is the scenario, i would like the user to input all the data and all and use em to populate a result. I won't need to store them in a database since i will just be showing them a result page.
I did http://railscasts.com/episodes/219-active-model and made my model tableless.
But now i have a problem when i wanna receive image upload from the user. I would also like to display that picture in the result page, and since i will just be using it once, if possible i wouldnt wanna store it in the database as well.
I tried implementing paperclip with the tableless model (since i couldnt find any other solution) but it seems that the model has inherit ActiveRecord::Base for it to work...
Is this possible? Or is this other way i can implement this?
Thanks!
If you were to succeed with using Paperclip for this, how would you get rid of the uploaded image once you no longer needed it? Without a database or some other form of persistent storage, how would you know where the image had been stored?
I think that you have some conceptual issues here that you should rethink before you start hacking up tableless models that accept image uploads.
But, if for some reason you really want to do it this way, then I would suggest just uploading the image without the benefit of a gem like Paperclip, which is really intended to make it easier to associate files with ActiveRecord objects. Just google for how you upload a file in Ruby, it's not really all that difficult.
OK, so you want to receive an image, and then display it right back, and not store the image. Can do.
What about a Controller that receives a file using multipart, and then transmits the file back to the request?
Controller file:
def upload
# Save file
name = params['datafile'].original_filename
directory = "tmp/uploads"
temp_file_name = File.join(directory, name)
send_file temp_file_name, :status=>200
end
You'll then just cleanup tmp when you need. Or, try out doing a File.delete temp_file_name when you need to.
If you want to validate that it's an image, you can do Paperclip model validation.