当在图像上未选择任何内容时,Primefaces裁剪器错误

时间:2013-03-17 16:14:40

标签: jsf-2 primefaces

当我尝试使用primefaces cropper裁剪图像时出现以下错误,但图像上没有选择任何内容(选择不存在):

2013年3月17日下午5:10:46 com.sun.faces.lifecycle.ProcessValidationsPhase执行 警告:负宽度或零宽度

java.awt.image.RasterFormatException: negative or zero width
        at java.awt.image.Raster.<init>(Raster.java:1108)
        at java.awt.image.WritableRaster.<init>(WritableRaster.java:129)
        at sun.awt.image.SunWritableRaster.<init>(SunWritableRaster.java:129)
        at sun.awt.image.ByteComponentRaster.<init>(ByteComponentRaster.java:154)
        at sun.awt.image.ByteInterleavedRaster.<init>(ByteInterleavedRaster.java:191)
        at sun.awt.image.ByteInterleavedRaster.createWritableChild(ByteInterleavedRaster.java:1261)
        at java.awt.image.BufferedImage.getSubimage(BufferedImage.java:1173)
        at org.primefaces.component.imagecropper.ImageCropperRenderer.getConvertedValue(ImageCropperRenderer.java:146)
        at javax.faces.component.UIInput.getConvertedValue(UIInput.java:1030)
        at javax.faces.component.UIInput.validate(UIInput.java:960)
        at javax.faces.component.UIInput.executeValidate(UIInput.java:1233)
        at javax.faces.component.UIInput.processValidators(UIInput.java:698)
        at com.sun.faces.context.PartialViewContextImpl$PhaseAwareVisitCallback.visit(PartialViewContextImpl.java:510)
        at com.sun.faces.component.visit.PartialVisitContext.invokeVisitCallback(PartialVisitContext.java:183)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1612)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIForm.visitTree(UIForm.java:362)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
        at com.sun.faces.context.PartialViewContextImpl.processComponents(PartialViewContextImpl.java:378)
        at com.sun.faces.context.PartialViewContextImpl.processPartial(PartialViewContextImpl.java:253)
        at javax.faces.context.PartialViewContextWrapper.processPartial(PartialViewContextWrapper.java:183)
        at javax.faces.context.PartialViewContextWrapper.processPartial(PartialViewContextWrapper.java:183)
        at javax.faces.context.PartialViewContextWrapper.processPartial(PartialViewContextWrapper.java:183)
        at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:1171)
        at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:76)
        at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
        at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:79)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.omnifaces.filter.FacesExceptionFilter.doFilter(FacesExceptionFilter.java:56)
        at org.omnifaces.filter.HttpFilter.doFilter(HttpFilter.java:75)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.omnifaces.filter.GzipResponseFilter.doFilter(GzipResponseFilter.java:148)
        at org.omnifaces.filter.HttpFilter.doFilter(HttpFilter.java:75)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.omnifaces.filter.CharacterEncodingFilter.doFilter(CharacterEncodingFilter.java:115)
        at org.omnifaces.filter.HttpFilter.doFilter(HttpFilter.java:75)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
        at org.apache.coyote.ajp.AjpProcessor.process(AjpProcessor.java:200)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:579)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:722)

Mar 17, 2013 5:10:46 PM com.sun.faces.renderkit.html_basic.OutcomeTargetRenderer getNavigationCase
WARNING: JSF1090: Navigation case not resolved for component j_idt74.

我真的不知道如何解决这个问题,因为看起来在裁剪过程中不会出现错误。

你能帮我解决这个问题吗?我真的不知道该怎么做。

我使用的是Primefaces 3.5和Myfaces 2.1.17。

非常感谢。

更新: cropper bean:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.imageio.stream.FileImageOutputStream;
import javax.servlet.ServletContext;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.primefaces.model.CroppedImage;

import com.meinanliegen.backingbean.User;
import com.meinanliegen.handlers.filters.Configurations;

@ManagedBean
@ViewScoped
public class ImageCropperBean implements Serializable {
  private static final long serialVersionUID = -7835902358418363820L;
  private static final Logger LOGGER = Logger.getLogger(ImageCropperBean.class);
  private CroppedImage croppedImage;
  private String photoCropped;
  // This only contains the relative path to the temp image : /temp/croppedImage
  private String relativeImagePhotoCropped;
  private String originalUploadedPhotoFileName;
  @ManagedProperty(value = "#{user}")
  private User user;

  public CroppedImage getCroppedImage() {
    return croppedImage;
  }

  public void setCroppedImage(CroppedImage croppedImage) {
    this.croppedImage = croppedImage;
  }

  public String getPhotoCropped() {
    return photoCropped;
  }

  public void setPhotoCropped(String photoCropped) {
    this.photoCropped = photoCropped;
  }

  public String getRelativeImagePhotoCropped() {
    return relativeImagePhotoCropped;
  }

  public void setRelativeImagePhotoCropped(String relativeImagePhotoCropped) {
    this.relativeImagePhotoCropped = relativeImagePhotoCropped;
  }

  public User getUser() {
    return user;
  }

  public void setUser(User user) {
    this.user = user;
  }

  public String crop() {
    if (croppedImage == null) {
      LOGGER.error("The cropped image is null");
    }
    else {
      manageTheCrop();
    }
    return null;
    }

  private void manageTheCrop() {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    ServletContext servletContext = (ServletContext) facesContext.getExternalContext().getContext();

    String originalFilePhotoName = croppedImage.getOriginalFilename();
    String tempLocationForCroppedFile = servletContext.getRealPath(Configurations.UPLOAD_TEMP_FILE_PATH);

    String prefix = FilenameUtils.getBaseName(originalFilePhotoName);
    String suffix = FilenameUtils.getExtension(originalFilePhotoName);

    // First time we need to save the original photo name
    if (originalUploadedPhotoFileName == null) {
      originalUploadedPhotoFileName = prefix + "." + suffix;
    }

    try {
      // We will keep the same name for cropped photo, until the user decides
      File photoCroppedFile = File.createTempFile(prefix, "." + suffix, new File(tempLocationForCroppedFile));
      photoCropped = photoCroppedFile.getPath();
      cropImageAddContentTo(photoCropped);

      String prefixCroppedPhoto = FilenameUtils.getBaseName(photoCropped);
      String suffixCroppedPhoto = FilenameUtils.getExtension(photoCropped);
      String photoCroppedFileName = prefixCroppedPhoto + "." + suffixCroppedPhoto;
      relativeImagePhotoCropped = Configurations.UPLOAD_TEMP_FILE_PATH + photoCroppedFileName;
    }
    catch (IOException ex) {
      LOGGER.error(ex);
      BeanUtils.addMessage(FacesMessage.SEVERITY_ERROR, "Bitte versuchen Sie es noch einmal.",
      "Fehler beim Zuschneiden Ihres Fotos");
    }
      }

public void saveCroppedImageAsPrimaryUserImage() {
if (photoCropped != null) {
  String profilePhotoChosenFilePrefix = String.format("%s_%s_%s", user.getFirstName(), user.getLastName(),
      originalUploadedPhotoFileName);
  try {
    // We will save the last cropped image as profile
    File finalPhotoLocation = new File(Configurations.UPLOAD_FILE_PATH, profilePhotoChosenFilePrefix);

    OutputStream output = null;
    output = new FileOutputStream(finalPhotoLocation);
    IOUtils.copy(new FileInputStream(photoCropped), output);
    user.setTempProfilePictureSrc(relativeImagePhotoCropped);
    user.setProfilePictureSrc("../uploads/" + finalPhotoLocation.getName());
  }
  catch (IOException ex) {
    LOGGER.error(ex);
    BeanUtils.addMessage(FacesMessage.SEVERITY_ERROR, "Bitte versuchen Sie es noch         einmal.", "Fehlermeldung");
      }
    }
  }

  public void cropImageAddContentTo(String croppedImagePath) {
    try {
      cropImage(croppedImagePath);
    }
    catch (IOException ex) {
      LOGGER.error(ex);
      BeanUtils.addMessage(FacesMessage.SEVERITY_ERROR, "Fehlermeldung",
          "Es ist eiin Fehler aufgetreten. Bitte versuchen Sie es noch einmal.");
    }
  }

  public void cropImage(String imageFileName) throws IOException {
    FileImageOutputStream imageFileOutput = null;
    imageFileOutput = new FileImageOutputStream(new File(imageFileName));
    imageFileOutput.write(croppedImage.getBytes(), 0, croppedImage.getBytes().length);
    imageFileOutput.close();
  }
}

和JSF部分:

<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">

<p:row>
    <p:column style="height:120px; width 120px;">
        <p:panelGrid columns="2">
            <p:row>
                <p:column>
                    <div class="photo">
                        <p:graphicImage id="standardUploadedImage" value="#{user.tempProfilePictureSrc}" />
                    </div>
                </p:column>
            </p:row>
            <p:row>
                <h:panelGroup id="adjustImage">
                    <p:column style="width:150px">
                        <p:panelGrid>
                            <p:row>
                                <p:column>
                                    <h:outputText value="Bild innerhalb des Rahmens positionieren"
                                        rendered="#{uploadPhotoHandler.userImageUploaded == true}" />
                                </p:column>
                            </p:row>
                            <p:row>
                                <p:column>
                                    <p:commandButton value="Bild anpassen" oncomplete="cropWidget.show()"
                                        update=":growl cropDialog" immediate="true"
                                        rendered="#{uploadPhotoHandler.userImageUploaded == true}"
                                        styleClass="css3button" />
                                </p:column>
                            </p:row>
                        </p:panelGrid>
                    </p:column>
                </h:panelGroup>
            </p:row>
        </p:panelGrid>
    </p:column>
</p:row>

<p:row>
    <p:column>
        <p:fileUpload fileUploadListener="#{uploadPhotoHandler.handleFileUpload}" mode="advanced"
            label="Portraitfoto hochladen" sizeLimit="5242880" allowTypes="/(\.|\/)(gif|jpe?g|png)$/"
            invalidSizeMessage="Datei darf maximal 5 MB groß sein"
            invalidFileMessage="Bitte prüfen Sie das Format. Es können nur jpg, gif, png Dateien verwendet werden."
            update=":growl standardUploadedImage uploadedImage imageCropper adjustImage" auto="true"
            required="false" />
    </p:column>
</p:row>

<p:row>
    <p:column>
        <p:confirmDialog id="cropDialog" header="Bild anpassen" severity="alert" showEffect="fade" closeOnEscape="true"
            closable="true" hideEffect="fade" widgetVar="cropWidget">
            <p:panelGrid>
                <p:row>
                    <p:column colspan="2">
                        <p:panelGrid columns="3" style="height:50px">
                            <p:row>
                                <p:column>
                                    <p:commandButton id="cancel" value="Löschen" onclick="cropWidget.hide()"
                                        type="button"
                                        update=":growl imageCropper uploadedImage standardUploadedImage"
                                        immediate="true" styleClass="css3button" />
                                </p:column>
                            </p:row>
                            <p:row>
                                <p:column>
                                    <h:panelGrid id="cropBtnPanel">
                                        <p:commandButton id="crop" value="Zuschneiden"
                                            action="#{imageCropperBean.crop}"
                                            update=":growl imageCropper uploadedImage standardUploadedImage cropFinished crop cropBtnPanel ApplyBtnPanel"
                                            process="crop imageCropper" styleClass="css3button"
                                            rendered="#{imageCropperBean.showCrop}" />
                                    </h:panelGrid>
                                </p:column>
                            </p:row>
                            <p:row>
                                <p:column>
                                    <h:panelGrid id="ApplyBtnPanel">
                                        <p:commandButton id="cropFinished" value="Übernehmen"
                                            actionListener="#{imageCropperBean.saveCroppedImageAsPrimaryUserImage}"
                                            oncomplete="cropWidget.hide()"
                                            update=":growl imageCropper uploadedImage standardUploadedImage crop cropFinished cropBtnPanel ApplyBtnPanel"
                                            process="cropFinished imageCropper" styleClass="css3button"
                                            rendered="#{imageCropperBean.showApply}" />
                                    </h:panelGrid>
                                </p:column>
                            </p:row>
                        </p:panelGrid>
                    </p:column>
                </p:row>

                <p:row>
                    <p:column headerText="Original">
                        <div class="photoToBeCropped">
                            <p:imageCropper id="imageCropper" 
                                            value="#{imageCropperBean.croppedImage}"
                                            image="#{user.tempProfilePictureSrc}" 
                                            initialCoords="225,75,300,125"
                                            aspectRatio="1.0" 
                                            rendered="#{uploadPhotoHandler.userImageUploaded == true}"
                                            minSize="170" 
                                            required="true" />
                        </div>
                    </p:column>
                    <p:column headerText="Cropped">
                        <div class="photoToBeCropped">
                            <p:graphicImage id="uploadedImage" value="#{imageCropperBean.relativeImagePhotoCropped}" />
                        </div>
                    </p:column>
                </p:row>
            </p:panelGrid>
        </p:confirmDialog>
    </p:column>
</p:row>

值得一提的是,整个裁剪模块都嵌入了一个向导主要步骤。

5 个答案:

答案 0 :(得分:7)

我可以在自己showcase上重现它。当我取消选择图像并单击 Crop 时,我在ajax响应中看到未处理的异常:

enter image description here

(如果他们安装了一个不错的ajax exception handler,那就太好了,所以最终用户可以立即看到它)

嗯,这真是他们自己代码中的错误。这是4天前已报告为issue 5349,目前尚未安排。

如果您真的迫不及待地想要修复它,那么最好是覆盖渲染器以跳过getConvertedValue()中的作业:

public class MyImageCropperRenderer extends ImageCropperRenderer {

    @Override
    public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {
        if (submittedValue == null || ((String) submittedValue).endsWith("_0_0")) {
            return null;
        }

        return super.getConvertedValue(context, component, submittedValue);
    }

}

提交的值采用x_y_w_h格式,其中_w_h在未选择任何内容时为_0_0。在这种情况下,上面的自定义渲染器将立即返回null,而不是继续转换作业,这会抛出该异常。

要使此渲染器运行,请在faces-config.xml中按如下方式注册,其中<renderer-class>是自定义渲染器的FQN:

<render-kit>
    <renderer>
        <component-family>org.primefaces.component</component-family>
        <renderer-type>org.primefaces.component.ImageCropperRenderer</renderer-type>
        <renderer-class>com.example.MyImageCropperRenderer</renderer-class>
    </renderer>
</render-kit>

答案 1 :(得分:1)

我使用了为BalusC发布的解决方案。但偶尔我会得到值“_2_0”或类似的东西。在这些情况下,错误再次发生。然后我解决了:

@Override
public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {
    if (submittedValue != null ){
        String[] submittedValueArray = ((String) submittedValue).split("_");
        if (!"0".equals(submittedValueArray[2]) && !"0".equals(submittedValueArray[3])) {
            return super.getConvertedValue(context, component, submittedValue);
        }           
    }
    return null;
}

答案 2 :(得分:0)

catch (RasterFormatException e) {
 FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "This crop size is not supported", null);
 throw new ErrorImageSizeException(message); }

使用构造函数

创建扩展ConverterException的ErrorImageSizeException
public ErrorImageSizeException(FacesMessage mensagem) {
    super(mensagem);
}

答案 3 :(得分:0)

我得到java.lang.NumberFormatException:对于输入字符串:&#34; NaN&#34;当我在更改裁剪区域之前按下按钮时。

<h:form id="editUserPhotoForm" > 
    <p:messages showDetail="true" autoUpdate="false" closable="true" />
        <h:panelGrid columns="2" rendered="#{not empty adminView.editUser}">
            <p:imageCropper value="#{adminView.croppedImage}"  image="#{adminView.userImagePath}" initialCoords="0,0,200,260" aspectRatio="0.77" minSize="200,260"/>
            <p:graphicImage value="#{adminView.imageUser}" width="200" cache="false" />
        </h:panelGrid>
        <p:commandButton value="crop" action="#{adminView.crop}" ignoreAutoUpdate="true" process="@form" update="@form" icon="ui-icon-scissors"/>
</h:form>

我使用了@BalusC发布的解决方案但添加了条件:

if (submittedValue == null || ((String) submittedValue).endsWith("_0_0") || ((String)submittedValue).contains("NaN"))

答案 4 :(得分:0)

根据@BalusC发布的解决方案,我实现了自己的解决方案,并在没有选择时保存整个图片:

@Override
public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {

    if (submittedValue != null ){
        String[] submittedValueArray = ((String) submittedValue).split("_");
        if (!"0".equals(submittedValueArray[2]) && !"0".equals(submittedValueArray[3])) {
            return super.getConvertedValue(context, component, submittedValue);
        }           
    }

    try {
        return super.getConvertedValue(context, component, buildDefaultSubmittedValue(context, component));
    } catch (IOException e) {
        throw new ConverterException("Error cropping the whole image");
    }
}

private String buildDefaultSubmittedValue(FacesContext context, UIComponent component) throws IOException {
    ImageCropper cropper = (ImageCropper) component;

    //remove query string
    String imagePath = cropper.getImage();
    int queryStringIndex = imagePath.indexOf("?");
    if(queryStringIndex != -1 ) {
        imagePath = imagePath.substring(0, queryStringIndex);
    }

    BufferedImage outputImage = getSourceImage(context, imagePath);
    return "0_0_" + outputImage.getWidth() + "_" + outputImage.getHeight();
}

private BufferedImage getSourceImage(FacesContext context, String imagePath) throws IOException {
    BufferedImage outputImage = null;
    boolean isExternal = imagePath.startsWith("http");

    if(isExternal) {
        URL url = new URL(imagePath);

        outputImage =  ImageIO.read(url);
    }
    else {
        ExternalContext externalContext = context.getExternalContext();

        outputImage = ImageIO.read(new File(externalContext.getRealPath("") + imagePath));
    }

    return outputImage;
}