如何使用JPA从PostgreSQL中读取bytea图像数据?

14

我有一份PostgreSQL数据库,其中有一个数据类型为“bytea”的“image”列。我无法修改列或数据库配置。JPA注释的POJO包含以下映射:

@Column(name="image")
private byte[] image;

返回数据的格式如下(仅为示例)

WF5ClN6RlpLZ0hJTUdNQ1FJWmkwcFVGSUdNQ0lDWUE5TUEvanRFeElwK2x0M2tBQUFBQVNVVk9SSzVDWUlJPQo=
当我将这些数据写入文件(.jpeg),照片查看器会显示“这是损坏的文件”。我也理解实际图像字节数据看起来与上面的示例不同。我阅读了一些博客,提到PostgreSQL对bytea数据应用十六进制转换。如何使用或不使用JPA将其恢复为原始数据?
数据库 - PostgresSQL 版本9.5.1
驱动程序
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>9.4-1205-jdbc41</version>
</dependency>

你的示例应该可以工作(如果你没有使用@Lob注释;请参见这个相关问题)。你是如何得到那个base64表示的?你确定那不是由视图相关服务输出(并可能转换)的东西吗?(请尝试调试@Entity,看看返回到image属性的内容以及在保存之前设置给该属性的内容)。 - pozs
你确定这是一张图片吗?它是如何存储的?同时请查看此链接:http://stackoverflow.com/q/17667480/1700321。 - Aleksandr M
@pozs 使用 Apache Codec Base64 类和 javax.xml.bind.DatatypeConverter 的 parseBase64Binary() 进行解码。 - Bhushan
5个回答

12

尝试使用 @Lob 注释您的实体。

@Lob
@Column(name="image")
private byte[] image;
如果你正在使用Hibernate实现,你可以在列中添加@Type(type="org.hibernate.type.BinaryType")
@Lob
@Column(name="image")
@Type(type="org.hibernate.type.BinaryType")
private byte[] image;

我认为如果你正在使用Hibernate,你必须添加@Type,否则在启动时会出现found [bytea (Types#BINARY)], but expecting [oid (Types#BLOB)]错误。 - Klesun

7

ImageEntity

package com.example;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class ImageEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name="image")
    private byte[] image;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public byte[] getImage() {
        return image;
    }

    public void setImage(byte[] image) {
        this.image = image;
    }
}

图像仓库

package com.example;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ImageRepository extends JpaRepository<ImageEntity, Long> {
}

测试

package com.example;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import junit.framework.TestCase;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestApplication.class)
public class ImageDaoTest {

    @Resource
    private ImageRepository imageRepository;

    @Test
    public void testImage() throws IOException {

        // Read an image from disk. Assume test.png exists
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        try (InputStream in = getClass().getResourceAsStream("test.png")) {
            int length;
            byte[] buffer = new byte[1024];
            while ((length = in.read(buffer)) != -1) out.write(buffer, 0, length);
        }

        byte[] image = out.toByteArray();

        // Store image to DB
        ImageEntity imageEntiry = new ImageEntity();
        imageEntiry.setImage(image);
        long imageEntiryId = imageRepository.save(imageEntiry).getId();

        // Retrieve image from DB
        ImageEntity resultImageEntiry = imageRepository.findOne(imageEntiryId);
        byte[] resultImage = resultImageEntiry.getImage();

        // Compare retrieved image with source image by byte to byte comparison
        for (int i = 0; i < resultImage.length; i++) {
            TestCase.assertEquals(image[i], resultImage[i]);
        }

    }

}

它可以对抗Postgres 9.5.0-1,使用9.4.1207.jre7 jdbc驱动程序。


5

返回的数据看起来像是经过base64编码的,你需要在写入文件之前将其解码为二进制数据。

如需更多关于解码的信息,请查看这里


是的,你说得对,它是64Base编码。谢谢。 下面添加了完整的代码,也许对其他人有用。 - Bhushan

2

我正在添加完整的代码,这可能对其他人有用(跳过try / catch),

String base64EncryptedImage = new String(image);
decoded = org.apache.commons.codec.binary.Base64.decodeBase64(base64EncryptedImage);
ImageOutputStream out = new FileImageOutputStream(new File("D://abc.png"));
out.write(decoded);
out.close();

0

插入一张图片,你可以使用:

  //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();

// Finally, commit the transaction.
conn.commit();

从大对象中检索图像:

// 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 = conn.prepareStatement("SELECT imgoid FROM imageslo WHERE imgname = ?");
ps.setString(1, "myimage.gif");
ResultSet rs = ps.executeQuery();
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();

// Finally, commit the transaction.
conn.commit();

1
我正在使用JPA,我不能像回到JDBC那样做所有这些事情。我认为这个解决方案仍然无法处理十六进制编码的数据。 - Bhushan
在PostgreSQL中,“bytea”类型和大对象是完全分开的。 - pozs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接