Paperclip在保存后重命名文件

如何在上传和保存文件后重命名文件? 我的问题是我需要自动解析有关文件的信息,以便提出文件应该像我的应用程序一样保存文件名,但我无法访问生成文件名所需的信息,直到记录为止。模型已保存。

例如,如果您的模型具有属性图像:

has_attached_file :image, :styles => { ...... } 

默认情况下,papepclip文件存储在/ system /:attachment /:id /:style /:filename中。

因此,您可以通过重命名每个样式然后更改数据库中的image_file_name列来完成它。

 (record.image.styles.keys+[:original]).each do |style| path = record.image.path(style) FileUtils.move(path, File.join(File.dirname(path), new_file_name)) end record.image_file_name = new_file_name record.save 

你看过回形针插补吗?

如果它是你可以在控制器中找到的东西(在它被保存之前),你可以使用控制器,模型和插值的组合来解决你的问题。

我有这个例子,我想根据它的MD5哈希命名一个文件。

在我的控制器中,我有:

 params[:upload][:md5] = Digest::MD5.file(file.path).hexdigest 

然后我有一个config/initializers/paperclip.rb

 Paperclip.interpolates :md5 do|attachment,style| attachment.instance.md5 end 

最后,在我的模型中,我有:

 validates_attachment_presence :upload has_attached_file :upload, :path => ':rails_root/public/files/:md5.:extension', :url => '/files/:md5.:extension' 

要添加到@ Voyta的答案,如果您使用带回形针的S3:

 (record.image.styles.keys+[:original]).each do |style| AWS::S3::S3Object.move_to record.image.path(style), new_file_path, record.image.bucket_name end record.update_attribute(:image_file_name, new_file_name) 

我的头像图像以用户slug命名,如果他们更改了他们的名字,我也必须重命名图像。

这就是我使用S3和回形针重命名我的头像图像的方法。

 class User < ActiveRecord::Base after_update :rename_attached_files_if_needed has_attached_file :avatar_image, :storage => :s3, :s3_credentials => "#{Rails.root}/config/s3.yml", :path => "/users/:id/:style/:slug.:extension", :default_url => "/images/users_default.gif", :styles => { mini: "50x50>", normal: "100x100>", bigger: "150x150>" } def slug return name.parameterize if name "unknown" end def rename_attached_files_if_needed return if !name_changed? || avatar_image_updated_at_changed? (avatar_image.styles.keys+[:original]).each do |style| extension = Paperclip::Interpolations.extension(self.avatar_image, style) old_path = "users/#{id}/#{style}/#{name_was.parameterize}#{extension}" new_path = "users/#{id}/#{style}/#{name.parameterize}#{extension}" avatar_image.s3_bucket.objects[old_path].move_to new_path, acl: :public_read end end end 

并添加另一个答案,这是我用于S3重命名的完整方法:

  def rename(key, new_name) file_name = (key.to_s+"_file_name").to_sym old_name = self.send(file_name) (self.send(key).styles.keys+[:original]).each do |style| path = self.send(key).path(style) self[file_name] = new_name new_path = self.send(key).path(style) new_path[0] = "" self[file_name] = old_name old_obj = self.send(key).s3_object(style.to_sym) new_obj = old_obj.move_to(new_path) end self.update_attribute(file_name, new_name) end 

使用:Model.find(#)。rename(:avatar,“test.jpg”)

我想捐赠我的“安全移动”解决方案,该解决方案不依赖于任何私有API,并防止因网络故障导致的数据丢失:

首先,我们为每种风格获得新旧路径:

 styles = file.styles.keys+[:original] old_style2key = Hash[ styles.collect{|s| [s,file.path(s).sub(%r{\A/},'')]} ] self.file_file_name = new_filename new_style2key = Hash[ styles.collect{|s| [s,file.path(s).sub(%r{\A/},'')]} ] 

然后,我们将每个文件复制到它的新路径。 由于默认路径包括对象ID和文件名,因此永远不会与其他文件的路径发生冲突。 但如果我们尝试重命名而不更改名称,这将失败:

 styles.each do |style| raise "same key" if old_style2key[style] == new_style2key[style] file.s3_bucket.objects[old_style2key[style]].copy_to(new_style2key[style]) end 

现在我们将更新的模型应用于DB:

 save! 

在我们创建新的S3对象之后但在删除旧的S3对象之前执行此操作非常重要。 如果数据库更新失败(例如网络拆分时间不正确),此线程中的大多数其他解决方案都可能导致数据丢失,因为此时文件将位于新的S3位置,但数据库仍指向旧位置。 这就是为什么我的解决方案在DB更新成功之后才删除旧的S3对象的原因:

 styles.each do |style| file.s3_bucket.objects[old_style2key[style]].delete end 

就像复制一样,我们不可能意外删除另一个数据库对象的数据,因为对象ID包含在路径中。 因此,除非您同时重命名相同的数据库对象A-> B和B-> A(例如2个线程),否则此删除将始终是安全的。

要添加到@Fotios的答案:

这是我认为制作自定义文件名的最佳方式,但如果您想要基于md5的文件名,您可以使用Paperclip中已有的指纹。

您所要做的就是将它放到config / initializers / paperclip_defaults.rb中

 Paperclip::Attachment.default_options.update({ # :url=>"/system/:class/:attachment/:id_partition/:style/:filename" :url=>"/system/:class/:attachment/:style/:fingerprint.:extension" }) 

没有必要在这里设置:path,因为默认情况下是这样的:

 :path=>":rails_root/public:url" 

我没有检查是否有必要,但如果它不起作用,请确保您的模型能够在数据库中保存指纹 – > 这里

我发现另一个方便的提示是使用rails控制台检查它是如何工作的:

 $ rails c --sandbox > Paperclip::Attachment.default_options .. > s = User.create(:avatar => File.open('/foo/bar.jpg', 'rb')) .. > s.avatar.path => "/home/groovy_user/rails_projectes/funky_app/public/system/users/avatars/original/49332b697a83d53d3f3b5bebce7548ea.jpg" > s.avatar.url => "/system/users/avatars/original/49332b697a83d53d3f3b5bebce7548ea.jpg?1387099146" 

以下迁移解决了我的问题。

avatar重命名为photo

 class RenamePhotoColumnFromUsers < ActiveRecord::Migration def up add_attachment :users, :photo # Add `avatar` method (from Paperclip) temporarily, because it has been deleted from the model User.has_attached_file :avatar, styles: { medium: '300x300#', thumb: '100x100#' } User.validates_attachment_content_type :avatar, content_type: %r{\Aimage\/.*\Z} # Copy `avatar` attachment to `photo` in S3, then delete `avatar` User.where.not(avatar_file_name: nil).each do |user| say "Updating #{user.email}..." user.update photo: user.avatar user.update avatar: nil end remove_attachment :users, :avatar end def down raise ActiveRecord::IrreversibleMigration end end 

希望能帮助到你 :)

另一个选项设置为默认值,适用于所有上传。

此示例将名称文件更改为web的’name default’,例如: test áé.jpgtest_ae.jpg

助手/ application_helper.rb

 def sanitize_filename(filename) fn = filename.split /(?<=.)\.(?=[^.])(?!.*\.[^.])/m fn[0] = fn[0].parameterize return fn.join '.' end 

创建config / initializers / paperclip_defaults.rb

 include ApplicationHelper Paperclip::Attachment.default_options.update({ :path => ":rails_root/public/system/:class/:attachment/:id/:style/:parameterize_file_name", :url => "/system/:class/:attachment/:id/:style/:parameterize_file_name", }) Paperclip.interpolates :parameterize_file_name do |attachment, style| sanitize_filename(attachment.original_filename) end 

放完这段代码后需要重启