문제

I have a requirement to display the image as part of the FieldGroup. This is for the functionality where the Image appears as normal on a web page, when in edit mode I need to edit this image value by providing an 'upload' option.

I have a Pojo with a property of type com.vaadin.ui.Image along with the other String and Integer values.

public Image getImage() {
    return image;
}

public void setImage(Image image) {
    this.image = image;
}

I need to work this Image as a normal form element, for example when I edit a form, I have an editable TextField to type in the String value and change it, the same way I intend to display an upload button where I would have an option to replace the existing image.

I have tried using EasyUploads addon for this purpose by intercepting the build method of FieldGroup and specifying the Image field as of type org.vaadin.easyuploads.UploadField

Like this;

 @Override
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected <T extends Field> T build(String caption, Class<?> dataType,
            Class<T> fieldType) throws BindException {

        T field = super.build(caption, dataType, fieldType);
        if (caption.equalsIgnoreCase("image")) {
            final UploadField imageField = new UploadField() {
                @Override
                protected void updateDisplay() {
                    final byte[] pngData = (byte[]) getValue();
                    String filename = getLastFileName();
                    String mimeType = getLastMimeType();
                    long filesize = getLastFileSize();
                    if (mimeType.equals("image/jpeg")) {
                        StreamSource imagesource = new ImageSource(pngData);
                        StreamResource resource = new StreamResource(
                                imagesource, "Uploaded File");

                        Embedded embedded = new Embedded("Image:" + filename
                                + "(" + filesize + " bytes)", resource);
                        getRootLayout().addComponent(embedded);
                    } else {
                        super.updateDisplay();
                    }
                }
            };
            imageField.setFieldType(FieldType.BYTE_ARRAY);
...

This however fails to display the already available image, errors out with the stacktrace:

Caused by: java.lang.IllegalArgumentException: Property type class com.vaadin.ui.Image is not compatible with UploadField
    at org.vaadin.easyuploads.UploadField.setPropertyDataSource(UploadField.java:1021)
    at com.vaadin.data.fieldgroup.FieldGroup.bind(FieldGroup.java:265)
    at com.vaadin.data.fieldgroup.BeanFieldGroup.bind(BeanFieldGroup.java:167)
    at com.vaadin.data.fieldgroup.FieldGroup.setItemDataSource(FieldGroup.java:106)
    at com.vaadin.data.fieldgroup.BeanFieldGroup.setItemDataSource(BeanFieldGroup.java:142)

Is there a cleaner way of using an Image as part of the FieldGroup in vaadin 7?

도움이 되었습니까?

해결책

I would suggest replacing in your Pojo the Image instance with a simple byte[], because looking through the UploadField code, I can't see any natural way of converting the result from the upload (which is either a byte[] or a File instance) into something else, using the FieldGroup like you asked.

If you look inside AbstractField.getValue(), you will see that the model value is eventually passed through a settable converter which would have normally helped you in this case (see com.vaadin.data.util.converter.Converter). But I think you are pretty much forced to use byte[] if you want to bind a image bean to the FieldGroup.

Anyway, if you DO replace with byte[] the following steps will help you:

  1. Create a custom FieldGroupFieldFactory that will create an UploadField if you want to bind to a byte[] property + passes a ValueChangeListener for the UploadField is done uploading:

    public class ImageEnhancedFieldFactory extends DefaultFieldGroupFieldFactory {

    private Property.ValueChangeListener fileUploadedListener;
    
    private ImageEnhancedFieldFactory(Property.ValueChangeListener fileUploadedListener) {
        this.fileUploadedListener = fileUploadedListener;
    }
    
    @Override
    public <T extends Field> T createField(Class<?> type, Class<T> fieldType) {
        if (byte[].class.equals(type)) {
            UploadField uploadField = new UploadField(UploadField.StorageMode.MEMORY);
            uploadField.setFieldType(UploadField.FieldType.BYTE_ARRAY);
            uploadField.setButtonCaption("Change image");
            uploadField.addListener(fileUploadedListener);
            return (T) uploadField;
        }
        return super.createField(type, fieldType);
    }
    

    }

  2. Create an Image instance that shows the content of the byte[] from the pojo:

        final ImagePojo imagePojo = new ImagePojo();
    imagePojo.setName("superman");
    imagePojo.setImageContent(new byte[0]);
    
    BeanItem<ImagePojo> item = new BeanItem<ImagePojo>(imagePojo);
    
    
    final StreamResource imageResource = new StreamResource(new StreamResource.StreamSource() {
        @Override
        public InputStream getStream() {
            return new ByteArrayInputStream(imagePojo.getImageContent());
        }
    }, "myimage");
    imageResource.setCacheTime(0);
    
    final Image image = new Image("Image", imageResource);
    addComponent(image);
    

NOTE: its necessary to set the cache time to 0 in order to prevent the browser from caching the resource( see https://vaadin.com/book/vaadin7/-/page/components.embedded.html in the section Generating and Reloading Images)

3.Create the FieldGroup (with the new FieldGroupFieldFactory set) and bind to the properties of the pojo, including the one that contains the image content (the byte[]):

FieldGroup fieldGroup = new FieldGroup(item);
    fieldGroup.setFieldFactory(new ImageEnhancedFieldFactory(new Property.ValueChangeListener() {
        @Override
        public void valueChange(Property.ValueChangeEvent event) {
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
            String filename = "myfilename-" + df.format(new Date()) + ".jpg";
            imagePojo.setImageContent((byte[])event.getProperty().getValue());
            image.markAsDirty();
            imageResource.setFilename(filename);
        }
    }));

    addComponent(fieldGroup.buildAndBind("Image name", "name"));
    addComponent(fieldGroup.buildAndBind("Image content", "imageContent"));

I left a snippet on Gist of a component that you can paste in you UI and play around if you need (https://gist.github.com/gabrielruiu/9953279)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top