-->

How to read bytea image data from PostgreSQL with

2019-03-29 19:12发布

问题:

I have PostgreSQL database and there is column 'image' with datatype 'bytea'. I cannot modify columns or database configurations. JPA annotated POJO contains followign mapping

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

The returned data is in following format (this is just a sample)

WF5ClN6RlpLZ0hJTUdNQ1FJWmkwcFVGSUdNQ0lDWUE5TUEvanRFeElwK2x0M2tBQUFBQVNVVk9SSzVDWUlJPQo=

When I write this data to file (.jpeg) photo viewer says "this is corrupted file". I also understand that actual image byte data looks different from above sample. I read some blogs which mentioned that PostgreSQL applies hexadecimal conversion to bytea data. How to restore it to original data with or without JPA ?

Database - PostgresSQL Version 9.5.1

Driver

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>9.4-1205-jdbc41</version>
</dependency>

回答1:

The returned Data looks as if it is base64 encoded. You have to decode it back to binary data before writing to file.

For further information to decode look here



回答2:

Try to annotate you entity with @Lob

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

If you are using hibernate implementation you can add @Type(type="org.hibernate.type.BinaryType") in column too.

@Lob
@Column(name="image")
@Type(type="org.hibernate.type.BinaryType")
private byte[] image;


回答3:

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;
    }
}

ImageRepository

package com.example;

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

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

Test

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]);
        }

    }

}

It works against Postgres 9.5.0-1 with 9.4.1207.jre7 jdbc driver.



回答4:

I am adding complete code which may be useful for others (skipping 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();


回答5:

insert an image, you would use:

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

Retrieving the image from the Large Object:

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