rails paperclip S3具有动态存储桶名称

时间:2021-12-25 10:44:45

I'm using paperclip to upload my documents to Amazon S3. I would like to automatically create a bucket with the ID of my projects when I upload a new document.

我正在使用paperclip将我的文档上传到Amazon S3。我想在上传新文档时自动创建一个包含项目ID的存储桶。

Therefore, in my controller, I have this:

因此,在我的控制器中,我有这个:

 def new
    @pmdocument = Pmdocument.new
    @pmdocument.projectmilestone_id=params[:projectmilestone_id]

where projectmilestone_id is the foreign_key to my project (to be used as my bucket name)

其中projectmilestone_id是我项目的foreign_key(用作我的桶名)

My model is like this:

我的模型是这样的:

class Pmdocument < ActiveRecord::Base
  belongs_to :projectmilestone
  attr_accessible :id, :name, :description, :projectmilestone_id, :pmdoc, :projectmilestone_attributes
  attr_protected :pmdoc_content_type, :pmdoc_size
  accepts_nested_attributes_for :projectmilestone, :allow_destroy => false
  has_attached_file :pmdoc,
    :storage => :s3,
    :bucket => self.projectmilestone_id.to_s,
    :s3_credentials => File.join(Rails.root, 'config', 's3.yml')

When I load the page, I get this error: undefined method `projectmilestone_id' for #

当我加载页面时,我收到此错误:未定义的方法`projectmilestone_id'for#

I checked my controller and the projectmilestone_id field is correctly loaded there.

我检查了我的控制器,并在那里正确加载了projectmilestone_id字段。

I tried to change the bucket line to :bucket => self.name and then the error is gone.

我试图将存储桶行更改为:bucket => self.name,然后错误消失了。

The model works ok because projectmilestone_id is correctly stored in the db.

该模型工作正常,因为projectmilestone_id正确存储在db中。

My guess is that it could be linked to the accessible attributes but it seems to be ok too.

我的猜测是它可以链接到可访问的属性,但它似乎也可以。

What's wrong? Many thanks!!!

怎么了?非常感谢!!!


I really dont get it:

我真的不明白:

I decided not to change my bucket anymore (bad idea anyway as the name need to be unique for all S3) but to change my path instead.

我决定不再改变我的桶了(不管怎样,坏主意因为所有S3的名称都必须是唯一的),而是改变我的路径。

This is the code:

这是代码:

:path => proc { |attachment| "#{attachment.istance.projectname}/:attachment/:id/:basename.:extension" },

The first folder with my project name is not created. If I replace projectname by name, or even description (another field of pmdocuments), it works, but not with projectname. Of course, I checked that projectname is correctly populated. The reason is elsewhere.

未创建包含项目名称的第一个文件夹。如果我按名称或甚至描述(pmdocuments的另一个字段)替换projectname,它可以工作,但不能用projectname。当然,我检查了projectname是否正确填充。原因在于其他地方。

Any clue?

1 个解决方案

#1


10  

The has_attached_file method is executed in the context of the class (when the file is loaded), not in the context of a record instance where you could use attributes and other instance method. self.name works indeed, but it returns the name of the class ("Pmdocument"), not the name of a record.

has_attached_file方法在类的上下文中执行(在加载文件时),而不是在可以使用属性和其他实例方法的记录实例的上下文中执行。 self.name确实有效,但它返回类的名称(“Pmdocument”),而不是记录的名称。

But Paperclip is kind enough to allow what you want. The documentation on the S3 storage says:

但Paperclip非常友好,可以满足您的需求。有关S3存储的文档说:

You can define the bucket as a Proc if you want to determine it’s name at runtime. Paperclip will call that Proc with attachment as the only argument.

如果要在运行时确定其名称,可以将存储桶定义为Proc。 Paperclip会将附件作为唯一参数调用Proc。

In your case it would be something like this:

在你的情况下,它将是这样的:

has_attached_file :pmdoc,
  :storage => :s3,
  :bucket => proc { |attachment| attachment.instance.projectmilestone_id.to_s },
  :s3_credentials => File.join(Rails.root, 'config', 's3.yml')

Now you pass a Proc to has_attached_file. The content of the block is not evaluated while your class is loaded, but later when it's needed. Paperclip then calls the block with the attachment as argument and uses the returned value as bucket name.

现在您将Proc传递给has_attached_file。在加载类时不会评估块的内容,但在以后需要时会对其进行评估。 Paperclip然后使用附件作为参数调用块,并使用返回的值作为存储桶名称。

Edit:

Unfortunately, this block is run when the file is assigned, not when the record is saved. So all your attributes may not be set yet (the order of the assignment of the attributes when you do Pmdocument.new(params[:pmdocument]) is undetermined). I'd like Paperclip to work another way, but in the meantime I see 2 options:

不幸的是,这个块在分配文件时运行,而不是在保存记录时运行。因此,您的所有属性可能尚未设置(当您执行Pmdocument.new(params [:pmdocument]时)属性分配的顺序未确定)。我想让Paperclip以另一种方式工作,但与此同时我看到两个选择:

You can remove the file from the params in the controller and set it when everything else is ready:

您可以从控制器中的参数中删除该文件,并在其他所有准备就绪时进行设置:

pmdoc = params[:pmdocument].delete(:pmdoc)
@pmdocument = Pmdocument.new(params[:pmdocument])
@pmdocument.pmdoc = pmdoc

Or you can delay Paperclip post-processing by disabling it with before_post_process (see the Events section of the README) and running it in an after_save callback.

或者,您可以通过使用before_post_process禁用Paperclip后处理(请参阅README的“事件”部分)并在after_save回调中运行它来延迟Paperclip后处理。

#1


10  

The has_attached_file method is executed in the context of the class (when the file is loaded), not in the context of a record instance where you could use attributes and other instance method. self.name works indeed, but it returns the name of the class ("Pmdocument"), not the name of a record.

has_attached_file方法在类的上下文中执行(在加载文件时),而不是在可以使用属性和其他实例方法的记录实例的上下文中执行。 self.name确实有效,但它返回类的名称(“Pmdocument”),而不是记录的名称。

But Paperclip is kind enough to allow what you want. The documentation on the S3 storage says:

但Paperclip非常友好,可以满足您的需求。有关S3存储的文档说:

You can define the bucket as a Proc if you want to determine it’s name at runtime. Paperclip will call that Proc with attachment as the only argument.

如果要在运行时确定其名称,可以将存储桶定义为Proc。 Paperclip会将附件作为唯一参数调用Proc。

In your case it would be something like this:

在你的情况下,它将是这样的:

has_attached_file :pmdoc,
  :storage => :s3,
  :bucket => proc { |attachment| attachment.instance.projectmilestone_id.to_s },
  :s3_credentials => File.join(Rails.root, 'config', 's3.yml')

Now you pass a Proc to has_attached_file. The content of the block is not evaluated while your class is loaded, but later when it's needed. Paperclip then calls the block with the attachment as argument and uses the returned value as bucket name.

现在您将Proc传递给has_attached_file。在加载类时不会评估块的内容,但在以后需要时会对其进行评估。 Paperclip然后使用附件作为参数调用块,并使用返回的值作为存储桶名称。

Edit:

Unfortunately, this block is run when the file is assigned, not when the record is saved. So all your attributes may not be set yet (the order of the assignment of the attributes when you do Pmdocument.new(params[:pmdocument]) is undetermined). I'd like Paperclip to work another way, but in the meantime I see 2 options:

不幸的是,这个块在分配文件时运行,而不是在保存记录时运行。因此,您的所有属性可能尚未设置(当您执行Pmdocument.new(params [:pmdocument]时)属性分配的顺序未确定)。我想让Paperclip以另一种方式工作,但与此同时我看到两个选择:

You can remove the file from the params in the controller and set it when everything else is ready:

您可以从控制器中的参数中删除该文件,并在其他所有准备就绪时进行设置:

pmdoc = params[:pmdocument].delete(:pmdoc)
@pmdocument = Pmdocument.new(params[:pmdocument])
@pmdocument.pmdoc = pmdoc

Or you can delay Paperclip post-processing by disabling it with before_post_process (see the Events section of the README) and running it in an after_save callback.

或者,您可以通过使用before_post_process禁用Paperclip后处理(请参阅README的“事件”部分)并在after_save回调中运行它来延迟Paperclip后处理。