-2

Hey there i m trying to save an object which name is "Avatar" and then retrieve the object and display it's picture "image". i got this result : enter image description here

my Avatar Class:

@Entity
@Table(name="avatar")
public class Avatar implements java.io.Serializable {

    @Id
    private Integer avatarId;
    @Column(name = "image")
    private byte[] image;
//getters and setters

How i m saving the object:

session.getTransaction().begin();

        File file = new File("PicturePath");
        byte[] bFile = new byte[(int) file.length()];

        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            fileInputStream.read(bFile);
            fileInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        Avatar avatar = new Avatar();
        avatar.setAvatarId(1);
        avatar.setImage(bFile);
        session.save(avatar);
        session.getTransaction().commit();

Retrieving the data from Avatar Table:

public List<Avatar> avatarList(){
        session.getTransaction().begin();
        List<Avatar> avatar = session.createQuery("from Avatar").list();
        session.getTransaction().commit();
        return avatar;
    }

Controller:

 @FXML
    public TableView<Avatar> produits;

    @FXML
    public void initialize(){

        Task<ObservableList<Avatar>> task = new Task<ObservableList<Avatar>>() {
            @Override
            protected ObservableList<Avatar> call() throws Exception {
                return FXCollections.observableArrayList(CategorieTrait.getInstance().avatarList());
            }
        };


        produits.itemsProperty().bind(task.valueProperty());

        new Thread(task).start();
    }

My FXML file:

<TableView style="-fx-backround-color:#33d9b2;" fx:id="produits" prefWidth="900" prefHeight="600">

            <columnResizePolicy>
                <TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>

            </columnResizePolicy>
            <columns>

                <TableColumn text="Picture" style="-fx-backround-color:#33d9b2">
                    <cellValueFactory>
                        <PropertyValueFactory property="image"/>
                    </cellValueFactory>
                </TableColumn>
            </columns>
        </TableView>

I was expecting the actual picture to display in the tableview.

Bill Tsagkas
  • 530
  • 1
  • 4
  • 15
  • If you want to show picture, provide picture to tableview, and not byte array. Take a look on this question for example https://stackoverflow.com/questions/17067481/javafx-2-tableview-different-cell-factory-depending-on-the-data-inside-the-cel – user1516873 Jan 17 '20 at 12:20
  • I'm not sure the logic you use to read the file will work in all cases. The method blocks until data is available, but there's no mention in the javadoc that is blocks until **all** data is available. Taking a look at the implementation of this kind of logic in the java api this is also not an assumption that they make. I recommend using that implementation btw instead of implementing reading the data into the `byte[]` array yourself: [`Files.readAllBytes`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/Files.html#readAllBytes(java.nio.file.Path)) – fabian Jan 17 '20 at 15:44
  • That is not to say that storing the data in a `byte[]` array in memory is actually a good idea. I just wanted to add this comment to post some alternatives for this kind of logic, should you need it for some other purpose in the future. – fabian Jan 17 '20 at 15:46
  • thank you all for your comments so i have to code the cells myself with java right ? – Informatique Technologies Jan 17 '20 at 15:56
  • What format is the image field's array in? As in, is it just the raw pixel data or is it, for instance, in PNG or JPG format? – Slaw Jan 17 '20 at 19:27

1 Answers1

4

The reason why you can't see the image is because although you're correctly providing your TableView with the bytes of the image, you don't tell it how to render them. What you need is a custom TableCell that renders the byte array into an ImageView. So I would do the following:

First add an id property in your TableColumn in your fxml file:

<TableColumn fx:id="imageColumn" text="Picture" style="-fx-backround-color:#33d9b2">
    <cellValueFactory>
        <PropertyValueFactory property="image"/>
    </cellValueFactory>
</TableColumn>

Then reference it inside your Controller and add a CellFactory to it:

@FXML
public TableView<Avatar> produits;

@FXML
public TableColumn<Avatar, byte[]> imageColumn;

@FXML
public void initialize() {
    ...
    imageColumn.setCellFactory(param -> new TableCell<Avatar, byte[]>() {

        private ImageView imageView = new ImageView();

        @Override
        protected void updateItem(byte[] item, boolean empty) {
            super.updateItem(item, empty);
            if (item == null || empty) {
                setText(null);
                setGraphic(null);
            } else {
                imageView.setImage(getImageFromBytes(item));
                setGraphic(imageView);
            }
            this.setItem(item);
        }
    });
    ...
}

The CellFactory creates a table cell for each of your Avatar items, and the updateItem() method of that cell converts the bytes to an Image which in turn is rendered into an ImageView (if the cell is not empty). Here's a way to convert an array of bytes to a JavaFX Image:

private Image getImageFromBytes(byte[] imgBytes) {
    try {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(imgBytes);
        BufferedImage bufferedImage = ImageIO.read(inputStream);
        return SwingFXUtils.toFXImage(bufferedImage, null);
    } catch (IOException e) {
        e.printStackTrace();
    } 
    return null;
}

Important Note: I would avoid calling thegetImageFromBytes() method from inside the updateItem(), especially for a TableView with lots of records. I just did that for simplicity's sake. You might want to convert the images beforehand, and maybe store them in a map or something.

Edit: Changed the updateItem() method to not create a new ImageView each time it is called.

Bill Tsagkas
  • 530
  • 1
  • 4
  • 15
  • ... and your note is very important: the general rule is to never-ever do any potentially expensive loading/calculation in updateItem, doing so will work straight against its design intention (which is extreme re-use). Instead do it once in the model (could be a wrapper round the actual data or a synthetic column) – kleopatra Jan 18 '20 at 11:22
  • @kleopatra You're right, my mistake. I edited my answer to remove the ImageView creation as you mentioned. – Bill Tsagkas Jan 18 '20 at 16:31
  • 1
    that's good - and thanks for emphazising the note a bit more :) – kleopatra Jan 18 '20 at 16:34