2

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

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

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

4

1 回答 1

5

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

迁移

正如 Jim Deville 在评论中指出的那样,您可以在表中指定自定义类型列,例如:

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

如果您需要做更多非标准的事情,您还可以execute("SQL GOES HERE;")使用直接 SQL 来创建表。而且,如果您有现有的旧架构或在迁移之外进行的 SQL 更改,请考虑使用 structure.sql(config.active_record.schema_format = :sql选项 inconfig/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 文档“任何在事务结束时保持打开的大型对象描述符都将自动关闭。” (感谢 Diogo 提供的信息)

    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(参见)。

identifier是您需要传入/设置或仅通过执行connection.lo_creat.

其他示例/信息:

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

简单的方法

只需使用CarrierWavecarrierwave-postgresql

于 2013-02-07T15:21:23.817 回答