ImageCropper can work tightly with FileUpload handling images on the fly.
<div class="card">
    <h:form>
        <p:growl id="messages" showDetail="true"/>
        <p:fileUpload mode="advanced"
                      multiple="false"
                      update="messages cropperPanel"
                      listener="#{cropUploaderBean.handleFileUpload}">
            <p:validateFile sizeLimit="102400"
                            allowTypes="/(\.|\/)(gif|jpeg|jpg|png)$/"/>
        </p:fileUpload>
        <p:outputPanel id="cropperPanel">
            <h:panelGrid columns="1" rendered="#{not empty cropUploaderBean.originalImageFile}">
                <p:commandButton value="Crop"
                                 action="#{cropUploaderBean.crop}"
                                 update="cropped messages" styleClass="my-3"/>
                <h:panelGrid columns="2" cellpadding="7">
                    <p:imageCropper value="#{cropUploaderBean.croppedImage}"
                                    id="imageCropper" cache="false"
                                    image="#{cropUploaderBean.image}"
                                    initialCoords="50,50,150,100"
                                    minSize="50,50" maxSize="350,350"/>
                    <p:outputPanel id="cropped">
                        <p:graphicImage cache="false"
                                        rendered="#{not empty cropUploaderBean.croppedImage}"
                                        value="#{cropUploaderBean.cropped}"/>
                    </p:outputPanel>
                </h:panelGrid>
            </h:panelGrid>
        </p:outputPanel>
    </h:form>
</div>
package org.primefaces.showcase.view.multimedia;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.CroppedImage;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;
import org.primefaces.model.file.UploadedFile;
import java.io.ByteArrayInputStream;
import java.io.Serializable;
import jakarta.enterprise.context.SessionScoped;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Named;
@Named
@SessionScoped
public class CropUploaderBean implements Serializable {
    private CroppedImage croppedImage;
    private UploadedFile originalImageFile;
    public CroppedImage getCroppedImage() {
        return croppedImage;
    }
    public void setCroppedImage(CroppedImage croppedImage) {
        this.croppedImage = croppedImage;
    }
    public UploadedFile getOriginalImageFile() {
        return originalImageFile;
    }
    public void handleFileUpload(FileUploadEvent event) {
        this.originalImageFile = null;
        this.croppedImage = null;
        UploadedFile file = event.getFile();
        if (file != null && file.getContent() != null && file.getContent().length > 0 && file.getFileName() != null) {
            this.originalImageFile = file;
            FacesMessage msg = new FacesMessage("Successful", this.originalImageFile.getFileName() + " is uploaded.");
            FacesContext.getCurrentInstance().addMessage(null, msg);
        }
    }
    public void crop() {
        if (this.croppedImage == null || this.croppedImage.getBytes() == null || this.croppedImage.getBytes().length == 0) {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error",
                    "Cropping failed."));
        }
        else {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Success",
                    "Cropped successfully."));
        }
    }
    public StreamedContent getImage() {
        return DefaultStreamedContent.builder()
                .contentType(originalImageFile == null ? null : originalImageFile.getContentType())
                .stream(() -> {
                    if (originalImageFile == null
                            || originalImageFile.getContent() == null
                            || originalImageFile.getContent().length == 0) {
                        return null;
                    }
                    try {
                        return new ByteArrayInputStream(originalImageFile.getContent());
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                        return null;
                    }
                })
                .build();
    }
    public StreamedContent getCropped() {
        return DefaultStreamedContent.builder()
                .contentType(originalImageFile == null ? null : originalImageFile.getContentType())
                .stream(() -> {
                    if (croppedImage == null
                            || croppedImage.getBytes() == null
                            || croppedImage.getBytes().length == 0) {
                        return null;
                    }
                    try {
                        return new ByteArrayInputStream(this.croppedImage.getBytes());
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                        return null;
                    }
                })
                .build();
    }
}