2013-02-07 25 views
2

我需要在我的Rails应用程序中将大对象保存在数据库中。我认为我可以在SQL创建一个表作为在Rails中建模PostgreSQL的大对象

CREATE TABLE files (
id serial NOT NULL, 
name string NOT NULL, 
blob_oid oid NUT NULL 
) 

然后在Ruby中的数据存储为

conn.exec("BEGIN") 
lo = conn.lo_import(data) 
conn.exec("COMMIT") 
file = File.new 
file.name = file_name; file.blob_oid = lo.id 
file.save 

首先,这是正确的?其次,我如何描述File in Rails的模型。 Rails中的Friend.blob_oid的数据类型是什么?我可以使用Rails中的迁移来创建文件表吗?

+0

您确定您需要大物件吗?大多数时候使用'bytea'来存储“BLOB”数据更合适。 –

+0

@a_horse_with_no_name,我想我可以使用bytea或文本(我正在存储文本文件),但不会加载ActiveRecord对象内的整个文件?我认为这可能会造成记忆问题。用pg在Rails中存储这些文件的好方法是什么? – highBandWidth

回答

5

如果使用带有其中一个适配器的Rails附带的ActiveRecord,那么通常在通过其native_database_types方法返回的适配器中的NATIVE_DATABASE_TYPES常量中定义数据库类型到Rails或Ruby类型的唯一正式映射。对于Rails 3.2.x中的PostgreSQL,即ActiveRecord::ConnectionAdapters::PostgreSQLAdapterhere。因此,对于该适配器,Rails中的“二进制”类型映射到PG中的“bytea”类型。对于某些类型,您可以使用名为activerecord-native_db_types_override的gem覆盖它映射到的数据库类型。但是,我们要使用大型对象,所以......

迁移

吉姆德维尔在评论中指出的那样,你可以在表中指定类型的列,如自定义:

t.column :some_oid, 'blob_oid', :null => false 

如果您需要做更多非标准的事情,您还可以使用execute("SQL GOES HERE;")使用直接SQL来创建表。而且,如果您有现有的遗留模式或SQL迁移之外的更改,请考虑使用structure.sql(config.active_record.schema_format = :sql选项config/application.rb,然后执行:rake db:structure:dump)。

大对象读/写/检查长度/删除

一些修改复制澄清等来自:https://github.com/diogob/carrierwave-postgresql/blob/v0.1.0/lib/carrierwave/storage/postgresql_lo.rb

更新:我们可以,但不必把在lo_read/lo_write/lo_lseek之前开始并且在lo_close中确保阻止,因为根据PG documentation“在交易结束时保持打开的任何大对象描述符都将自动关闭。” (感谢迪奥戈该信息)

require 'pg' 

    ... 

    def read 
     (...).transaction do 
     lo = connection.lo_open(identifier) 
     content = connection.lo_read(lo, file_length) 
     connection.lo_close(lo) 
     content 
     end 
    end 

    def write(file) 
     (...).transaction do 
     lo = connection.lo_open(identifier, ::PG::INV_WRITE) 
     size = connection.lo_write(lo, file.read) 
     connection.lo_close(lo) 
     size 
     end 
    end 

    def delete 
     connection.lo_unlink(identifier) 
    end 

    def file_length 
     (...).transaction do 
     lo = connection.lo_open(identifier) 
     size = connection.lo_lseek(lo, 0, 2) 
     connection.lo_close(lo) 
     size 
     end 
    end 

代替connection,使用从模型或碱,例如原始连接ActiveRecord::Base.connection.raw_connection(见this)。

(...).transaction正在模型或基础上调用交易,例如, ActiveRecord::Base.transaction(参见this)。

identifier是您要么通过/设置或从connection.lo_creat获得的oid。

其他例子/信息:

后者和一些答案here建议你可能要考虑的大文件存储从分离DB,例如以便您可以使用云存储。但是,如果仅将路径/ ID存储到数据库管理的外部文件而不是,则会失去ACID一致性(一个或多个数据库记录可能指向一个或多个不存在的文件或一个或多个文件可能存在数据库中没有一个或多个关联记录)。将文件存储在文件系统上的另一个参数是可以对文件进行流式处理,但PG大对象以postgres管理的方式将文件存储在文件系统中,以确保ACID一致性并允许流式处理(您无法使用正常的BLOB/Rails二进制类型)。所以,这取决于;有些人发现使用路径引用存储在单独的存储中是一个更好的选择,有些更喜欢通过大对象的ACID一致性。

简单的方法

只需使用CarrierWavecarrierwave-postgresql

+1

您可以使用非正常迁移类型的普通迁移:http://guides.rubyonrails.org/migrations.html#supported-types –

+0

谢谢,吉姆!更新了答案。 –