您的位置:寻梦网首页编程乐园数据库PostgreSQL 7.2 Documentation

8.6. 存储二进制数据

PostgreSQL 提供两种不同的方法存储二进制数据. 二进制数据可以使用 PostgreSQL 的二进制数据类型 bytea 存储在表中,或者使用 大对象 特性,该特性以一种特殊的格式将二进制数据存储在一个独立的表中, 然后通过在你的表中保存一个指向该表的类型为 OID 的数值 来引用它.

为了判断那种方法比较合适,你必须理解每种方法的局限. bytea 数据类型并不适合存储非常大数量的二进制数据. 虽然类型为 bytea 的字段可以存储最多 1G 字节的二进制数据, 但是这样它会要求数量巨大的内存( RAM )来处理这样巨大 的数值.存储二进制的大对象的方法更适合存储非常大的数值, 但也有自己的局限.特别是删除一个包含大对象的行并未删除大对象. 删除大对象是一个需要处理的独立的操作.大对象还有一些安全性的问题, 因为人和联接到数据库的人都可以查看或者更改大对象,即使他们 没有查看/更新包含大对象的行的权限也一样.

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 API ,或者使用 getBLOB() setBLOB() 方法.

Important: 对于 PostgreSQL 而言,你必须在 一次 SQL 事务内访问大对象.你应该使用 带 false 输入参数的 setAutoCommit() 方法打开一个事务.

注意: 在将来的 JDBC 驱动中, getBLOB() setBLOB() 方法可能不再操作大对象,而是将处理 bytea 数据类型. 因此如果你要用大对象,我们建议你使用 LargeObject API

Example 8-4. 二进制数据例子

比如,假设你有一个表包含一幅图像和它的文件名, 并且你还想在 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 stream in some way here
        }
    rs.close();
}
ps.close();

这里的二进制数据是以 byte[] 形式检索的.你也可以使用一个 InputStream

另外你可能会需要存储一个非常大的文件,因此希望使用 LargeObject API 存储该文件∶

CREATE TABLE imagesLO (imgname text, imgOID OID);

要插入一个图像,你可以用∶

// 所有大对象 API 调用都必须在一次事务中
conn.setAutoCommit(false);

// 获取大对象管理器以便进行操作
LargeObjectManager lobj = ((org.postgresql.Connection)conn).getLargeObjectAPI();

//创建一个新的大对象
int oid = lobj.create(LargeObjectManager.READ | LargeObjectManager.WRITE);

//打开一个大对象进行写
LargeObject obj = lobj.open(oid, LargeObjectManager.WRITE);

// 现在打开文件
File file = new File("myimage.gif");
FileInputStream fis = new FileInputStream(file);

// 从文件拷贝数据到大对象
byte buf[] = new byte[2048];
int s, tl = 0;
while ((s = fis.read(buf, 0, 2048)) > 0)
{
      obj.write(buf, 0, s);
      tl += s;
}

// 关闭大对象
obj.close();

//现在向 imgesLO 插入行
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
conn.setAutoCommit(false);

// Get the Large Object Manager to perform operations with
LargeObjectManager lobj = ((org.postgresql.Connection)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();