↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

不受支持的版本: 7.4 / 7.3 / 7.2
历史版本PostgreSQL 7.4 已于 2010 年 10 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本手册首页。

31.7. 存储二进制数据 #

PostgreSQL 提供两种不同的 存储二进制数据的方式。二进制数据可以使用 bytea 数据类型存储在一个表中,或者使用大对象 特性——它把二进制数据以一种特殊格式存储在一个单独的 表中,并通过在你的表中存储一个 oid 类型的值来引用该表。

为了确定哪种方法合适,你 需要理解每种方法的限制。 bytea 数据类型并不适合存储非常 大量的二进制数据。虽然 bytea 类型的列最多可以容纳 1 GB 的二进制数据,但处理 这样大的值需要 大量的内存。用大对象方法 存储二进制数据更适合存储非常大的值, 但它也有自己的限制。具体来说,删除一个 包含大对象引用的行并不会删除该大对象。 删除大对象是一个需要单独执行的 操作。大对象还有一些安全性 问题,因为任何连接到数据库的人都可以查看 和/或修改任何大对象,即使他们没有 查看/更新包含该大对象引用的行的权限。

7.2 版是第一个支持 bytea 数据类型的 JDBC 驱动 版本。与之前的版本相比,7.2 中引入的这一功能 带来了一个行为上的变化。从 7.2 开始, getBytes()、setBytes()、 getBinaryStream() 和 setBinaryStream() 方法操作 bytea 数据类型。而在 7.1 及更早的版本中,这些方法操作 与大对象相关联的 oid 数据类型。 通过把 Connection 对象上的 compatible 属性设置为 7.1,可以把驱动恢复为旧的 7.1 行为。

要使用 bytea 数据类型,你只需使用 getBytes()、setBytes()、 getBinaryStream() 或 setBinaryStream() 方法。

要使用大对象功能,你可以使用 PostgreSQL JDBC 驱动提供的 LargeObject 类,或者使用 getBLOB() 和 setBLOB() 方法。

重要

你必须在一个 SQL 事务块内访问大对象。你可以通过调用 setAutoCommit(false) 来启动一个事务块。

注意

在 JDBC 驱动的一个未来版本中, getBLOB() 和 setBLOB() 方法可能不再 与大对象交互,而是改为处理 bytea 数据类型。因此,如果你打算使用大对象,建议 使用 LargeObject API。

例 31.8 包含一些关于 如何用 PostgreSQL JDBC 驱动处理二进制数据的例子。

例 31.8. 在 JDBC 中处理二进制数据

例如,假设你有一个表存储图像的文件名, 并且你还想把图像本身存储在一个 bytea 列中:

CREATE TABLE images (imgname text, img bytea);

要插入一幅图像,你可以使用:

File file = new File("myimage.gif");
FileInputStream fis = new FileInputStream(file);
PreparedStatement ps = conn.prepareStatement("INSERT INTO images VALUES (?, ?)");
ps.setString(1, file.getName());
ps.setBinaryStream(2, fis, file.length());
ps.executeUpdate();
ps.close();
fis.close();

这里,setBinaryStream() 从一个流向 bytea 类型的列传输指定数量 的字节。如果图像的内容已经在一个 byte[] 中,这也可以用 setBytes() 方法来完成。

检索一幅图像甚至更容易。(我们在这里使用 PreparedStatement,但同样可以使用 Statement 类。)

PreparedStatement ps = con.prepareStatement("SELECT img FROM images WHERE imgname = ?");
ps.setString(1, "myimage.gif");
ResultSet rs = ps.executeQuery();
if (rs != null) {
    while (rs.next()) {
        byte[] imgBytes = rs.getBytes(1);
        // use the data in some way here
    }
    rs.close();
}
ps.close();

这里二进制数据是作为 byte[] 检索的。你也可以 改用一个 InputStream 对象。

又或者,你要存储一个非常大的文件,并想使用 LargeObject API 来 存储该文件:

CREATE TABLE imageslo (imgname text, imgoid oid);

要插入一幅图像,你可以使用:

// All LargeObject API calls must be within a transaction block
conn.setAutoCommit(false);

// Get the Large Object Manager to perform operations with
LargeObjectManager lobj = ((org.postgresql.PGConnection)conn).getLargeObjectAPI();

// Create a new large object
int oid = lobj.create(LargeObjectManager.READ | LargeObjectManager.WRITE);

// Open the large object for writing
LargeObject obj = lobj.open(oid, LargeObjectManager.WRITE);

// Now open the file
File file = new File("myimage.gif");
FileInputStream fis = new FileInputStream(file);

// Copy the data from the file to the large object
byte buf[] = new byte[2048];
int s, tl = 0;
while ((s = fis.read(buf, 0, 2048)) > 0) {
    obj.write(buf, 0, s);
    tl += s;
}

// Close the large object
obj.close();

// Now insert the row into imageslo
PreparedStatement ps = conn.prepareStatement("INSERT INTO imageslo VALUES (?, ?)");
ps.setString(1, file.getName());
ps.setInt(2, oid);
ps.executeUpdate();
ps.close();
fis.close();

从大对象中检索该图像:

// All LargeObject API calls must be within a transaction block
conn.setAutoCommit(false);

// Get the Large Object Manager to perform operations with
LargeObjectManager lobj = ((org.postgresql.PGConnection)conn).getLargeObjectAPI();

PreparedStatement ps = con.prepareStatement("SELECT imgoid FROM imageslo WHERE imgname = ?");
ps.setString(1, "myimage.gif");
ResultSet rs = ps.executeQuery();
if (rs != null) {
    while (rs.next()) {
        // Open the large object for reading
        int oid = rs.getInt(1);
        LargeObject obj = lobj.open(oid, LargeObjectManager.READ);

        // Read the data
        byte buf[] = new byte[obj.size()];
        obj.read(buf, 0, obj.size());
        // Do something with the data read here

        // Close the object
        obj.close();
    }
    rs.close();
}
ps.close();

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。