Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.ikasan.dashboard.ui.general.component;
import com.vaadin.flow.component.*;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.dom.DomEvent;
import com.vaadin.flow.dom.Element;
import com.vaadin.flow.server.InputStreamFactory;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.shared.Registration;
import org.ikasan.dashboard.ui.util.VaadimThreadFactory;
import java.io.InputStream;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
/**
* Extension of anchor, that will display a vaadin button as clickable instance to initiate a download. The download
* content is generated at click time.
*/
public class LazyDownloadButton extends Button {
private static final String DEFAULT_FILE_NAME = "download";
private static final Supplier DEFAULT_FILE_NAME_SUPPLIER = () -> DEFAULT_FILE_NAME;
private Supplier fileNameCallback;
private InputStreamFactory inputStreamCallback;
private Anchor anchor;
public LazyDownloadButton() {
}
public LazyDownloadButton(String text) {
super(text);
}
public LazyDownloadButton(Component icon) {
super(icon);
}
public LazyDownloadButton(String text, InputStreamFactory inputStreamFactory) {
this(text, DEFAULT_FILE_NAME_SUPPLIER, inputStreamFactory);
}
public LazyDownloadButton(Component icon, InputStreamFactory inputStreamFactory) {
this(icon, DEFAULT_FILE_NAME_SUPPLIER, inputStreamFactory);
}
public LazyDownloadButton(String text, Component icon, InputStreamFactory inputStreamFactory) {
this(text, icon, DEFAULT_FILE_NAME_SUPPLIER, inputStreamFactory);
}
public LazyDownloadButton(String text, Supplier fileNameCallback, InputStreamFactory inputStreamFactory) {
this(text, null, fileNameCallback, inputStreamFactory);
}
public LazyDownloadButton(Component icon, Supplier fileNameCallback, InputStreamFactory inputStreamFactory) {
this("", icon, fileNameCallback, inputStreamFactory);
}
/**
* Creates a download button. The first two parameters are used for the button displayment.
*
* The third callback is called, when the download button is clicked. This is called inside the UI thread before
* the input stream generation starts. It can be used for instance to deactivate the button.
*
* The fourth parameter is a callback, that is used to generate the download file name
*
* The fifth parameter is a callback to generate the input stream sent to the client. This callback will be
* called in a separate thread (so that the UI thread is not blocked).
*
* The sixth callback is called when the download anchor on the client side has been clicked (means the input
* stream content is now sent to the user). This callback is called inside the UI thread.
* BE AWARE THAT THE FILE IS STILL SENT TO THE CLIENT AT THIS POINT. Deleting or removing things can interrupt the
* download.
*
* @param text button text
* @param icon button icon
* @param fileNameCallback callback for file name generation
* @param inputStreamCallback callback for input stream generation
*/
public LazyDownloadButton(String text, Component icon, Supplier fileNameCallback, InputStreamFactory inputStreamCallback) {
super(text);
this.fileNameCallback = fileNameCallback;
this.inputStreamCallback = inputStreamCallback;
if (icon != null) {
setIcon(icon);
}
super.addClickListener(event -> {
// we add the anchor to download in the parent of the button - if there are scenarios where the anchor
// should be placed somewhere else, this needs to be extended. Cannot be placed inside of the button
// since the button might be disabled or invisible thus makes the anchor not usable.
// The anchor must not be removed by this component, since the download failes otherwise
getParent().ifPresent(component -> {
Objects.requireNonNull(fileNameCallback, "File name callback must not be null");
Objects.requireNonNull(inputStreamCallback, "Input stream callback must not be null");
if (anchor == null) {
anchor = new Anchor();
Element anchorElement = anchor.getElement();
anchorElement.setAttribute("download", true);
anchorElement.getStyle().set("display", "none");
component.getElement().appendChild(anchor.getElement());
anchorElement.addEventListener("click", event1 -> fireEvent(new DownloadStartsEvent(this, true, event1)));
}
Optional optionalUI = getUI();
Executors.newSingleThreadExecutor(new VaadimThreadFactory("LazyDownloadButton")).execute(() -> {
try {
InputStream inputStream = inputStreamCallback.createInputStream();
optionalUI.ifPresent(ui -> {
if (ui.isAttached()) {
ui.access(() -> {
StreamResource href = new StreamResource(fileNameCallback.get(), () -> inputStream);
href.setCacheTime(0);
anchor.setHref(href);
anchor.getElement().callJsFunction("click");
});
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
});
});
});
}
@Override
protected void onDetach(DetachEvent detachEvent) {
if (anchor != null) {
getParent().map(Component::getElement).ifPresent(parentElement -> {
Element anchorElement = anchor.getElement();
if (anchorElement != null && parentElement.getChildren().anyMatch(anchorElement::equals)) {
parentElement.removeChild(anchorElement);
}
});
}
}
public Registration addDownloadStartsListener(ComponentEventListener listener) {
return addListener(DownloadStartsEvent.class, listener);
}
public Supplier getFileNameCallback() {
return fileNameCallback;
}
public void setFileNameCallback(Supplier fileNameCallback) {
this.fileNameCallback = fileNameCallback;
}
public InputStreamFactory getInputStreamCallback() {
return inputStreamCallback;
}
public void setInputStreamCallback(InputStreamFactory inputStreamCallback) {
this.inputStreamCallback = inputStreamCallback;
}
public void reset() {
this.anchor = null;
}
public static class DownloadStartsEvent extends ComponentEvent {
private final DomEvent clientSideEvent;
/**
* Creates a new event using the given source and indicator whether the
* event originated from the client side or the server side.
*
* @param source the source component
* @param fromClient true if the event originated from the client
*/
public DownloadStartsEvent(LazyDownloadButton source, boolean fromClient, DomEvent clientSideEvent) {
super(source, fromClient);
this.clientSideEvent = clientSideEvent;
}
public DomEvent getClientSideEvent() {
return clientSideEvent;
}
}
}