In my Rails application users can upload Excel files. In my model there is a class ImportFile that uses attachment_fu like this:
class ImportFile < ActiveRecord::Base
has_attachment :storage => :file_system, :path_prefix => 'public/imports', :max_size => 10.megabytes
end
When user clicks "Add file" he enters a page with a <%= fields.file_field :uploaded_data %>. attachment_fu does it's job and file upload is being done (let's ommit validation problems). I want to keep this file for future so I copy uploaded file to other temp file. Temp file is working fine - I can see it on disk.
def self.write_to_tmp(data)
temp_file = Tempfile.new("import", "#{Rails.root}/tmp")
begin
temp_file.write(data)
ensure
temp_file.close(false)
end
temp_file
end
What I want to do is to show user a preview and then let him choose if he wants to add a file or discard it - there are two buttons. I have a problem when user chooses to save a file, because a temp file I've just created above is gone. It is deleted before request.
Does anyone has hints how to achive this? Or can point me to upload-with-preview file scenario like the one I've presened? I've been looking for days, but I've failed to find one.
The most reliable approach to this sort of thing would be to create a simple "upload" tracking model like you have there, but using Paperclip instead. This can be configured to handle a very large number of files.
You need to actually save these records for them to persist between requests. This will lead to orphaned records, but a simple cron job or rake task you can kill off all the unused files any time you need to.
Creating a large number of files in a single directory is usually a bad idea. Paperclip has a path parameter which will split up your ID number into parts, so record #903132 goes into .../90/31/32 for example.
Keep a regular attachment, and if they want to discard it, delete it, otherwise use it. At some point later clean out all the unused attachments.
Related
When an image is updated using carrierwave I have it setup to use the model id and original file name
def filename
"#{model.id}-#{original_filename}" if original_filename.present?
end
Its only like this because its the default way I found when I was setting up carrierwave. Recently I realised if you update an image the link to the image is broken because the filename is updated to use the newer files name. So if a page is linked to the old image it isn't updated with the newer image but instead becomes a broken image link.
I want it to keep the old file name when the image is being updated but cant find how to do this in the documentation and from googling around
Edit
I diddnt add a lot of the 'use case' because I diddnt think the problem was this complicated.
Basically on the site there are items that have images assigned to them. I did see the comment on not using only model.id and #struthersneil cleared up why that comment is there. Considering the images records for an items are created when the item is created they will never be new when the image is being added (because its just updating a record that already exists). Because of this I'd say my best bet is to use just model.id as the filename.
My other option is to keep the old image so the links aren't broken. The main problem for using this is that users create posts on the site and link to these images in the posts (the posts are regularly updated so I cant to a once off cache on the post and images in the post). The images become updated from new information on the items they are associated to. Once this happens the image linked in the post will show outdated information and they will have to manually update to the new image each time. So having the image link stay the same with the image being updated is the best option in this case particular case
original_filename in this context is the name of the file that was just received by the uploader. It's the file's original name.
By doing this:
"#{model.id}-#{original_filename}" if original_filename.present?
You're telling CarrierWave to use the new name of the file every time. It sounds like you want a given model to use the same filename every time. So the obvious option is simply to throw away the original_filename altogether and use the model id by itself.
However, the comment above the filename method in the auto-generated uploader code says this:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
Which leads us to this...
# Be careful using record ids as filenames. If the filename is stored in the database
# the record id will be nil when the filename is set. Don't use record ids unless you
# understand this limitation.
That is kind of a hitch. Unless you can guarantee that your model always has an id (which won't be true if it's new) then you shouldn't use model.id to name your file.
I would suggest you generate a unique file_identifier string once for each model on initialize (as long as the file_identifier has not already been set), and just use this unique string as your filename, e.g.
class Something < ActiveRecord::Base
mount_uploader :image, SomeImageUploader
after_initialize do
unless self.file_identifier
self.file_identifier = SecureRandom.hex(32)
end
end
end
...and in your uploader class...
def filename
model.file_identifier if original_filename
end
Now your uploaded image will always overwrite the same file. But notice: you have just lost your image extension. That seems to be fine in my modern version of Firefox (it can guess by the looking at the image header) but it's probably not the most widely compatible solution. You could take the extension of the original_filename and add it on there, but now you're back to your original problem if someone uploads a png and then uploads a jpg (the result would be two different files).
You could perform a conversion to a fixed format of your choice on upload, then the filename + extension would always be the same.
Honestly I think you'd have an easier time if you accept that files get a new name after every upload, and simply leave the old files hanging around for a set period of time to continue supporting users who have the old link. (How old are these links going to get anyway? If it's sitting in a cache, then why cache only the generated HTML without caching the image?) This is especially true if you ever plan to use a CDN service between your application and your users. The usual pattern is to version your files with a new unique filename every time, which the CDN will cache once and forever.
In a Ruby on Rails 3.2.12 project I want to store data for a model in static files.
I don't need to be able to write to the data through the app, and want to handle updating and adding new objects by changing the static file(s).
Is there a way to set up a model, and then say "look at this file (or folder) to find the records", rather than the database (the app also has a database)?
I can imagine the data being stored in two main ways:
all the records are kept in a .json or .yml file
each record is kept in an individual .yml or .markdown file, inside a specific folder (as in jekyll).
Examples: a glossary
My application has a glossary. Each entry in the glossary should be an individual record.
There will be a glossary index page (/glossary) that is just like your standard glossary, listing all the terms and definitions. Each individual record(term + definition) also needs to be individually retrievable for use in footnotes on other pages.
I want people to be able to edit the glossary by editing files through github. They could be pointed to a single file with all the records, or (preferably) an individual file with just the record they want to edit (less syntax to handle, less risk).
Key questions
How do you point the model to the static data file, rather than the database?
Where should the source directory be?
What would be the best storage format?
Thanks very much for your help!
ActiveHash would be a good solution for this. You could use the ActiveYaml configuration if you would like to store the data in a separate yaml file instead of directly in the model.
Unless someone comes up with a better solution or with a gem which can do this out of the box I would recommend building your own model with own find and save methods. Something like (untested):
class Term
attr_accessor :name, :definition
def self.find(name)
YAML.load(File.read("#{name}.yaml"))
end
def save
File.open("#{name}.yaml", 'w'){ |f| f.write(YAML.dump(self)) }
end
end
The format for saving is up to you, see here or here for more info. Since you want the users to change the files I would go with whichever you find the most user friendly.
I have a model Item that has an attribute :code.
Items are added to the database via CSV file uploads in rails. Each :item should have a product image associated with it.
The research I've done so far seems to suggest that bulk image uploads (think 500-1000 images) are best handled outside of rails.
My question is this: if I upload bulk images to S3, is there any way to associate images to their respective :item? For simplicity, let's assume that we can easily infer :code from each images filename.
The end goal is to display an items image with something like:
<%= image_tag("#{#item.image}") %>
Let me know if I can clarify, thank you!
You gave a little to few information (e.g. what gem do you use to store the images) for an optimal answer. So here is the answer for the given question: Add the following method into your Item model:
def image
"example_image_#{self.code}.jpg"
end
Please be aware that image_tag() will always result into an asset pipeline path. Please see http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html#method-i-image_tag
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.