I'm using Thoughtbot's Paperclip gem to handle file uploads.
I'm finding that when I upload a file with spaces in the filename, it gets stored with the spaces replaced with underscores.
That's good.
I also tried uploading a file with special characters like ~ and so on and they all got replaced with underscores.
Great. Exactly what I want.
But why is it happening?
All I'm doing in my model is...
has_attached_file(
file_somefile,
:path => ":rails_root/public/system/other/path/elements/:basename.:extension"
)
Is this Paperclip's default behavior?
To add a little more information, this happens in Paperclip::Attachment#cleanup_filename in which the default restricted_characters /[&$+,/:;=?#<>[]{}\|\\^~%# ]/ are replaced with underscores.
It's not documented, but you can specify the :restricted_characters option to paperclip to change what gets replaced, e.g.
class User < ActiveRecord::Base
attr_accessible :avatar
has_attached_file :avatar, :restricted_characters => /#/ # only replaces '#'
end
OK, after a little more searching, I found this blog post that says, down at the bottom, that Paperclip actually does some minimal processing of filenames.
Related
I'm trying to do something super simple in AMS where I generate the url attribute for an object like below:
class DeckSerializer < ActiveModel::Serializer
attributes :id, :title, :description, :url
has_one :user
has_many :cards
def url
deck_url(object)
end
end
However, I get the following error:
ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true
I'm currently using Rails 4.2.0 and AMS 0.9.3
Anyone know what's going on?
This is a DIY way to solve this problem. Create a field on the model to store the url. then after the object has been saved, you update it with a manually generated url like this.
here is how i solved it.
if #questionsolution.save
generatedurl = 'http://localhost:3000/questionsolutions/' + #questionsolution.id.to_s
#questionsolution.update(solutionurl: generatedurl)
end
then you can get the url from the resource direclty without depending on active model serializers to do it for you.
Turns out it's a known bug mentioned here:
https://github.com/rails-api/active_model_serializers/issues/573
I switched the AMS version to 0.8.3 and everything worked. While not a complete solution, it works for the time being. Interested in hearing thoughts from others
You have to put Rails.application.routes.default_url_options[:host] = 'localhost:3000' in config/environments/development.rb
I'm currently building an app which has a model Post. I'm using the paperclip gem to upload images, and everything is going well.
class Post < ActiveRecord::Base
has_attached_file :headerimage, styles: {banner => "400x300#"}
end
As you can see by my class above, if I were to get a Post object, I could get the banner image with the following in my view:
image = Post.first.headerimage(:banner)
Alias
However, in my app, it must have the image attribute image refer to the thumbnail image. So, in my models class, I wrote
class Post < ActiveRecord::Base
has_attached_file :headerimage, styles: {banner => "400x300#"}
alias_attribute :image, :headerimage
end
which allows me to get an image by calling the following:
image = Post.first.image
This is what I want - however, it gets the original image from paperclip, so it is equivalent to writing the following:
image = Post.first.headerimage instead of image = Post.first.headerimage(:banner)
How can I set up a proper alias_attribute to access the paperclip thumnail? I can't seem to find an answer anywhere else, and I am unsure how paperclip is actually working.
I thought I might logically be able to do something like
alias_attribute :image, :headerimage(:banner)
but that does not work.
You can just try this - basically call the original with arguments since aliases won't take parameter but we know the fixed parameter to pass:
alias :image, :headerimage
def headerimage(name=nil)
headerimage(name || :thumb)
end
I am trying to find a way to limit the size of files that users can upload, and it seems to be possible with javascript, but what bothers me is that what happens if a user just turns javascript off and upload like 1GB file to the server, and I did find a way to validate it on server side but it takes place once the file has been uploaded, which isnt an option. So I am trying to find the right way to do it but the best thing I came up with is to create a form for file uploading with javascript and make validations on client side but I guess there is still a way around it...
I am using RoR and CarrierWave if that matters ...
EDIT
1)Everything I can find on this page is to use size validation which happens after the file has been uploaded
2) I use that validation by carrierwave but again, it takes place once the file has been uploaded, it doesnt make sense to wait an hour till some huge file gets uploaded just to know its too big
You can use a Rails custom validator to verify your attachment meets specific file size requirements.
Grab a copy of the validator from https://gist.github.com/1009861 and save it to your lib/ folder as file_size_validator.rb. Add the error translations to config/locales/en.yml or wherever is appropriate for your setup. Then do this in your parent model:
# app/models/brand.rb
require 'file_size_validator'
class Brand < ActiveRecord::Base
mount_uploader :logo, BrandLogoUploader
validates :logo,
:presence => true,
:file_size => {
:maximum => 0.5.megabytes.to_i
}
end
Like validates_length_of, validates_file_size accepts :maximum, :minimum, :in [range], and :is options.
Another solution
A custom validator could also be used like this.
app/models/user.rb
class User< ActiveRecord::Base
attr_accessible :product_upload_limit
has_many :products
end
app/models/brand.rb
class Product < ActiveRecord::Base
mount_uploader :file, FileUploader
belongs_to :user
validate :file_size
def file_size
if file.file.size.to_f/(1000*1000) > user.product_upload_limit.to_f
errors.add(:file, "You cannot upload a file greater than #{upload_limit.to_f}MB")
end
end
end
Here, the upload limit varies from user to user & is saved in the user model.
The problem was solved by nginx, which works like a buffer so if somebody is trying to upload a 1GB file, it will go to your buffer first so it won't block your unicorn instance until it's completely uploaded, so that's one good thing. The other is that you can set the max content-length parameter in your nginx config file, so if you set to 5 Megabytes and somebody is trying to upload 1 GB file, it'll be denied. But again, I am not sure how it works, if it just checks the http header, then I have a feeling that somebody can simply tamper with this value and fool your nginx.
I have a project using Paperclip gem for attachments and Globalize3 for attribute translation. Records need to have a different attachment for each locale.
I though about moving Paperclip attributes to translation table, and that might work, but I don't think that would work when Paperclip needs to delete attachments.
What's the best way to achieve something like that?
UPDATE: to be clear, I want this because my client wants to upload different images for each locale.
Unfortunately I didn't find a way to do this using Globalize3. In theory, I could have added a separate model for image and add image_id to list of translated columns (to have something like MainModel -> Translation -> Image), but it seems that Globalize has some migration issues with non-string columns.
Instead of using Globalize3, I did this with a separate Image model with locale attribute and main model which accepts nested attributes for it. Something along the lines of:
class MainModel < ActiveRecord::Base
has_many :main_model_images
accepts_nested_attributes_for :main_model_images
# return image for locale or any other as a fallback
def localized_image(locale)
promo_box_images.where(:locale => locale).first || promo_box_images.first
end
end
class MainModelImage < ActiveRecord::Base
belongs_to :main_model
has_attached_file :image
validates :locale,
:presence => true,
:uniqueness => { :scope => :main_model_id }
end
Tricky part was getting form to accept nested attributes only for one image, instead of all images in has_many relation.
=f.fields_for :main_model_images, #main_model.image_for_locale(I18n.locale) do |f_image|
=f_image.hidden_field :locale
=f_image.label :image
You could also try the paperclip-globalize3 gem, it should handle the case you describe. https://github.com/emjot/paperclip-globalize3
Ok since you asked me to share my solution to this problem even though I am using Carrierwave as a library for uploading here is it:
Ok so I would have a model setup like this:
class MyModel < ActiveRecord::Base
# ...
translates :attr_one, :attr_two, :uploaded_file
Now what I need for CarrierWave to work is place to attach the uploader to and that can be done on the Translation model
Translation.mount_uploader :uploaded_file, FileUploader
end
Now for your question about deleting, I think though I haven't needed to do it but it should work as the README says it should but on the translation model. https://github.com/jnicklas/carrierwave#removing-uploaded-files
MyModel.first.translation.remove_uploaded_file!
I haven't taken a look at paperclip for a good 2 years and if this is not applicable knowledge I suggest you try out carrierwave.
in Rails using the Polymorphic version of Paperclip, the default saving technique means that files with the same name overwrite each other. Including the :id in the path and URL doesn't work as it just overwrites the earlier file with the old :id.
I've tried interpolations using a time-stamp, but it just looks for the current time when being shown the image (plus as I have multiple thumbnails it takes longer than a second, therefore the images have different stamps).
Paperclip.interpolates :uniqueid do |attachment, style|
Time.now.to_i.to_s
end
Also tried using a hex number, but it iterates through for each thumb, thus breaking as there's a new hex value each time.
Paperclip.interpolates :uniqueid do |attachment, style|
ActiveSupport::SecureRandom.hex(3)
end
As it's the Polymorphic version, and thus has it's own model, I don't know how to access the values from the parent model (in this case "Post").
Variations on the code below all throw up "undefined method" errors.
Paperclip.interpolates :user_id do |attachment, style|
current_user.id
end
Sorry if it seems a newbie question, but it's well document for the traditional Paperclip, but nothing is out there for the Polymorphic fork.
How about including :class in the :path for has_attached_file ?
has_attached_file :attachment,
:path => ":rails_root/attachments/:class/:id/:attachment/:basename.:extension",
:url => "downloads/:id/:title.:extension"