|
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() 方法.
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();
|