简体   繁体   中英

Vaadin 23: file upload from clipboard / Ctrl+V

My need : I'd like to add an "upload from clipboard" functionality into a Vaadin 23 application so that the user can paste a screenshot into an Upload field.

Known pieces of the puzzle : I know that there is a paste event (see here https://stackoverflow.com/a/51586232/10318272 or here https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event ) and there's the Vaadin Upload component ( https://vaadin.com/docs/latest/components/upload ).

Question : How can I transfer the pasted data into the Upload field?

Why initially intended solution does not work : It seems that uploading a screenshot via an Upload field is not feasible because the FileList (= model of a file input field) does not allow to add/append a new File object.

(Working) Workaround : So my workaround is a TextArea with a paste -EventListener that does a remote procedure call of a @ClientCallable method at the server.

Left component is the TextArea , right component is a preview Image .

TextArea 加预览图

Code :

import com.vaadin.flow.component.ClientCallable;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.server.StreamResource;

import java.io.ByteArrayInputStream;
import java.util.Base64;

public class PasteScreenshotField extends HorizontalLayout {

private final Image previewImg;

public PasteScreenshotField() {
    // This could be any focusable type of component, I guess.
    TextArea textField = new TextArea();
    textField.setWidth("50px");
    textField.setHeight("50px");
    this.add(textField);

    String pasteFunction = "for(const item of event.clipboardData.items) {"
                           + "  if(item.type.startsWith(\"image/\")) {"
                           + "    var blob = item.getAsFile();"
                           + "    var reader = new FileReader();"
                           + "    reader.onload = function(onloadevent) {$1.$server.upload(onloadevent.target.result);};"
                           + "    reader.readAsDataURL(blob);"
                           + "  }"
                           + "}";
    this.getElement().executeJs("$0.addEventListener(\"paste\", event => {"+pasteFunction+"})", textField.getElement(), this);

    // Optional: Preview of the uploaded screenshot
    previewImg = new Image();
    // TODO: Fixed size of 50px x 50px stretches the image. Could be better.
    previewImg.setWidth("50px");
    previewImg.setHeight("50px");
    this.add(previewImg);
}

@ClientCallable()
private void upload(String dataUrl) {
    System.out.println("DataUrl: "+dataUrl);
    if (dataUrl.startsWith("data:")) {
        byte[] imgBytes = Base64.getDecoder().decode(dataUrl.substring(dataUrl.indexOf(',') + 1));
        // Showing a preview is just one of the possible scenarios.
        // TODO: check filename extension. Maybe it's not a png.
        previewImg.setSrc(new StreamResource("preview.png", () -> new ByteArrayInputStream(imgBytes)));
    }
}

}

Extendability : Instead of previewImg.setSrc you could do whatever you want with the uploaded file. The preview is just the proof that the screenshot goes to the server (and could go back to the client again).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM