Unable to load uploaded picture in JMIX 2.0

Hi,

I cant display image in JMIX 2.0 after upload image. It display the download link but not the image. Does this have to do with SecurityConfiguration? Im using FileStorageUploadField

Screenshot

Hello!

Could you clarify how do you set value to Image component?

For instance, if your FileStorageUploadField is bound with property in InstanceContainer, so you can do the same with Image:

<data>
    <instance id="documentDc"
              class="com.company.tr.entity.Document">
        <fetchPlan extends="_base"/>
        <loader/>
    </instance>
</data>
  ...
<layout>
    <formLayout id="form" dataContainer="documentDc">
        <fileStorageUploadField id="fileRefField"
                                property="fileRef"
                                fileNameVisible="true"/>
    </formLayout>
    <image id="image"
           width="200px"
           height="200px"
           themeNames="scale-down"
           dataContainer="documentDc"
           property="fileRef"/>
     ...
</layout>

Otherwise you should manually set value to Image component after uploading a file. For more details see: image :: Jmix Documentation.

Hye @pinyazhin, I am using jmix 2.0.1 with flowUI and i’m facing the same problem as mentioned above. I had tried manually to preview the uploaded image using .SetValueSource and .SetSrc before saving it to dB but the image does not load. If I am using Static Recourses based on the link you provided, the image is successfully load. Is there any ways/ workaround for me to solve the issue? Thanks, in advance!
**I even tried to use onUploadReceiptFileUploadSucceeded handlers, but the image did not load

Hello!

To show FileRef or byte[] types in Image, you can do the same as an example in docs with static resource. For FileRef you should get FileStorage and open input stream.

Example for FileStorageUploadField with IMMEDIATE put mode. For MANUAL mode use FileUploadSucceededEvent.

@Autowired
protected FileStorageLocator fileStorageLocator;

@ViewComponent
protected JmixImage<FileRef> image;

@Subscribe("fileRefField")
protected void onFileRefFieldComponentValueChange(final AbstractField.ComponentValueChangeEvent<FileStorageUploadField, FileRef> event) {
    FileRef fileRef = event.getValue();
    if (fileRef == null) {
        image.setSrc("");
    } else {
        image.setSrc(new StreamResource(fileRef.getFileName(),
                () -> fileStorageLocator.getByName(fileRef.getStorageName()).openStream(fileRef)));
    }
}

Hi,

using the following coding I can see the uploaded image directly in the GUI.
But, when I click on OK,
it never got written into the DB table.
The column type is byte[]

What do I miss to write the data back into the DB?

(photo3UploadField is a fileUploadField and the coding is called in …uploadSucceededListener)

           ByteArrayInputStream bais = new ByteArrayInputStream(photo3UploadField.getValueSource().getValue());
        BufferedImage newImage = Scalr.resize(ImageIO.read(bais), targetSize);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(newImage, "png", baos);
        byte[] bytes = baos.toByteArray();
        ByteArrayInputStream bais2 = new ByteArrayInputStream(bytes);
        StreamResource resource = new StreamResource("test.png", () -> bais2);
        photo1Field.setSrc(resource);

got it… I did not set the dataContainer in the image in the descriptor

Hi @pinyazhin ,

I did exactly the way the documentation and what u shown but still doesnt show anything.

Hi,

I tried using your solution using the onFileRefFieldComponentValueChange. When i implement it i get this error.

image

Could you share your definition of Image and FileStorageUploadField in XML and controller code where you trying to set value to the Image?

Hi,

This is my code

Dialog-view.XML code

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<view xmlns="http://jmix.io/schema/flowui/view"
      title="msg://dialogView.title">
  <data>
        <instance id="fuelDetails2Dc"
                  class="com.company.hexagonamdu.entity.FuelDetails2">
            <fetchPlan extends="_local">
                <property fetchPlan="_base" name="driverId"/>
                <property fetchPlan="_base" name="masterList"/>
                <property fetchPlan="_base" name="supplier"/>
            </fetchPlan>
        </instance>
    </data>

<formLayout id="form" dataContainer="fuelDetails2Dc">
 <details summaryText="Upload">
                            <hbox>
                                <fileStorageUploadField dataContainer="fuelDetails2Dc" required="true"
                                                        property="document_ref" label="Upload" id="uploadReceipt"
                                                        fileNameVisible="true"/>
                                <image id="receiptPic" property="document_ref" dataContainer="fuelDetails2Dc" height="10em" width="10em" themeNames="scale-down" />
        </hbox>
</details>
</formLayout>

DialogView.Java

package com.company.hexagonamdu.view.dialog;

import com.company.hexagonamdu.entity.FuelDetails2;
import com.company.hexagonamdu.entity.Supplier;
import com.company.hexagonamdu.entity.TruckDriver;
import com.company.hexagonamdu.entity.VehicleMasterList;
import com.company.hexagonamdu.view.fueldetails2.FuelDetails2DetailView;
import com.company.hexagonamdu.view.main.MainView;
import com.vaadin.flow.component.AbstractField;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.HasValue;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamResource;
import io.jmix.core.*;
import io.jmix.flowui.DialogWindows;
import io.jmix.flowui.Notifications;
import io.jmix.flowui.UiComponents;
import io.jmix.flowui.component.combobox.EntityComboBox;
import io.jmix.flowui.component.image.JmixImage;
import io.jmix.flowui.component.textfield.TypedTextField;
import io.jmix.flowui.component.upload.FileStorageUploadField;
import io.jmix.flowui.kit.component.button.JmixButton;
import io.jmix.flowui.view.*;
import org.springframework.beans.factory.annotation.Autowired;

import java.time.LocalTime;
import java.util.Date;

import static com.company.hexagonamdu.view.fueldetails2.FuelDetails2DetailView.setNewOdometerValue;

@Route(value = "DialogView", layout = MainView.class)
@ViewController("DialogView")
@ViewDescriptor("Dialog-view.xml")
public class DialogView extends StandardView {

   @ViewComponent
    private JmixButton saveButton;
   @Autowired
    private DataManager dataManager;
    @Autowired
    private Notifications notifications;
    @Autowired
    private Metadata metadata;
   @ViewComponent
    private TypedTextField receiptIdField;
    @ViewComponent
    private FileStorageUploadField uploadReceipt;
    @Autowired
    protected FileStorageLocator fileStorageLocator;
    @ViewComponent
    protected JmixImage<FileRef> image;

 @Subscribe
    protected void onInit(InitEvent event) {

        saveButton.addClickListener(this::validateAndSave);
}

  private void validateAndSave(final ClickEvent<Button> event) {

            // Create a new instance of FuelDetails2 entity
            FuelDetails2 fuelDetails = metadata.create(FuelDetails2.class);
            // Set properties of the fuelDetails entity based on the form values
            fuelDetails.setDocument_ref(uploadReceipt.getValue());

    try {
                // Save the fuelDetails entity to the database
                dataManager.save(fuelDetails);

                // Show a success notification
                notifications.show("Fuel details saved successfully.");

                // Call the updateDataGrid method to refresh the data grid
                getParent().ifPresent(view -> {
                    if (view instanceof FuelDetails2DetailView) {
                        ((FuelDetails2DetailView) view).updateDataGrid();
                    }
                });

                // Close the dialog
                closeWithDefaultAction();
            } catch (Exception e) {
                // Handle any exceptions that may occur during saving
                notifications.show("Error while saving fuel details: " + e.getMessage());
            }
        } else {
            // Display an error message or handle the validation failure
        }
    }

@Subscribe("uploadReceipt")
    protected void onFileRefFieldComponentValueChange(final AbstractField.ComponentValueChangeEvent<FileStorageUploadField, FileRef> event) {
        FileRef fileRef = event.getValue();
        if (fileRef == null) {
            image.setSrc("");
        } else {
            image.setSrc(new StreamResource(fileRef.getFileName(),
                    () -> fileStorageLocator.getByName(fileRef.getStorageName()).openStream(fileRef)));
        }
    }
} 

This is the FuelDetails2.java

image

Thank you in advance

Thank you for the provided code. Since you use StandardView, you should set FuelDetails2 instance to the container manually when View is opened. For instance:

@ViewComponent
protected InstanceContainer<FuelDetails2> fuelDetails2Dc;

@Subscribe
protected void onInit(final InitEvent event) {
    FuelDetails2 fuelDetails = dataManager.create(FuelDetails2.class);
    fuelDetails2Dc.setItem(fuelDetails);
}

Thus when you upload an image via FileStorageUploadField it automatically set picture to Image component. And you don’t need onFileRefFieldComponentValueChange() listener anymore.

Another way, just change:

@ViewComponent
protected JmixImage<FileRef> image;

To your image id:

@ViewComponent
protected JmixImage<FileRef> receiptPic;

And it should work.

Hi @pinyazhin , the images are able to preview now but we had issues when clicking to open the dialog button and when clicking the save button to save the details and images. When click to open the dialog box we hit this error:

java.lang.ClassCastException: class java.lang.String cannot be cast to class com.company.hexagonamdu.entity.TruckDriver (java.lang.String is in module java.base of loader ‘bootstrap’; com.company.hexagonamdu.entity.TruckDriver is in unnamed module of loader ‘app’)
at io.jmix.core.metamodel.model.utils.MethodsCache$SettersHolder.accept(MethodsCache.java:241)
at io.jmix.core.entity.BaseEntityEntry.setAttributeValue(BaseEntityEntry.java:104)
at io.jmix.core.entity.EntityValues.setValue(EntityValues.java:73)
at io.jmix.core.entity.EntityValues.setValueEx(EntityValues.java:198)
at io.jmix.core.entity.EntityValues.setValueEx(EntityValues.java:151)
at io.jmix.flowui.data.value.ContainerValueSource.setValue(ContainerValueSource.java:174)
at io.jmix.flowui.data.binding.impl.AbstractValueBinding.setValueToSource(AbstractValueBinding.java:282)
at io.jmix.flowui.data.binding.impl.AbstractValueBinding.onComponentValueChange(AbstractValueBinding.java:230)
at io.jmix.flowui.data.binding.impl.AbstractValueBinding.lambda$addComponentValueChangeListener$9601773c$1(AbstractValueBinding.java:376)
at com.vaadin.flow.component.ComponentEventBus.fireEventForListener(ComponentEventBus.java:233)
at com.vaadin.flow.component.ComponentEventBus.fireEvent(ComponentEventBus.java:222)
at io.jmix.flowui.component.textfield.TypedTextField.fireTypedValueChangeEvent(TypedTextField.java:267)
at io.jmix.flowui.component.textfield.TypedTextField.fireAllValueChangeEvents(TypedTextField.java:260)
at io.jmix.flowui.component.textfield.TypedTextField.setValueInternal(TypedTextField.java:189)
at io.jmix.flowui.component.textfield.TypedTextField.setValue(TypedTextField.java:174)
at com.company.hexagonamdu.view.dialog.DialogView.setDriverNameListField(DialogView.java:167)
at com.company.hexagonamdu.view.fueldetails2.FuelDetails2DetailView.onCustomParameterDialogButtonClick(FuelDetails2DetailView.java:597)

and when attempting to save we hit this error :

java.lang.NumberFormatException: For input string: “”
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:678)
at java.base/java.lang.Integer.parseInt(Integer.java:786)
at com.company.hexagonamdu.view.dialog.DialogView.validateAndSave(DialogView.java:238)

This is our code when we retrieve the data from previous row to compare with the new filled in data, hope this can help you to point out what are our mistakes.

public void onCustomParameterDialogButtonClick(final ClickEvent<JmixButton> event) {
        TruckDriver selectedDriver = driverNameListField.getValue();
        VehicleMasterList selectedMasterList = masterListField.getValue();
        String selectedDetailsDate = detailDateField.getValue();
        LocalTime selectedTime = timeField.getValue();

        if (selectedDriver != null && selectedMasterList != null && selectedDetailsDate != null && selectedTime != null) {
            dialogWindows.view(this, DialogView.class).open();

            // Pass any necessary data to the dialog
            DialogView.setLastOdometerValue(lastodometerField.getValue());
            DialogView.setDriverNameListField(String.valueOf(selectedDriver.getId()));
            DialogView.setMasterListField(String.valueOf(selectedMasterList.getId()));
            DialogView.setDetailDateField(detailDateField.getValue());
            DialogView.setTimeField(String.valueOf(timeField.getValue()));
        } else {
            // Display an error message if any of the fields is empty
            notifications.show("Please fill in 'Driver's Name', 'Registration No' fields, 'DetailDate' and 'Time'.");
        }
    }

We tried to perform some workaround with the code to counter the error but didn’t able to figure it out.
Truly appreciate if you can point out what mistakes that we made and share your insight on how to deal with this issues.
Thank you in advance.

Hello!

The first exception means that you are trying to set text value from TypedTextField (or textField in XML) to entity value (TruckDriver). If you want to bind field with TruckDriver type you should use entityPicker field.

The second exception says that cannot parse empty string to Integer. Try to debug your dialog to find out where mismatch in types comes from.