代码之家  ›  专栏  ›  技术社区  ›  Jonathon

ImageIO无法处理在单个用户的android手机上拍摄的照片

  •  2
  • Jonathon  · 技术社区  · 9 年前

    我有一个Android应用程序,可以拍摄照片,并使用以下方法将其转换为位图:

    private Bitmap generateBitmap(byte[] data) {
        Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
        Matrix mat = new Matrix();
        mat.postRotate(-90);
        return Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(),
                bmp.getHeight(), mat, true);
    }
    

    然后使用:

    bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
    

    这个 outputStream 然后发布(多部分/表单数据)到我的web服务器(jboss)。在服务器上,jax-rs和MultipartForm将其转换为data[]并发送以进行附加处理,该处理从以下开始:

    BufferedImage image = ImageIO.read(new ByteArrayInputStream(form.getImageFileData()));
    

    该应用程序目前正在生产中,并按预期运行。上周我们激活了一个新用户(三星Galaxy Note Edge),他上传的每一张照片都会生成 javax.imageio.IIOException: Error reading PNG image data 当我调用ImageIO时。read(),其中堆栈跟踪的根本原因是:

    Caused by: java.util.zip.ZipException: incorrect data check
    

    照片在浏览器中正常显示,但由于此异常,我无法完全处理它们。我也可以在编辑器中打开它们,将它们旋转360度,重新保存,然后处理它们。

    有人能帮我理解在这一台设备上是什么导致了这个问题,或者建议我可以在服务器上做些什么来解决这个问题,同时仍然生成 BufferedImage 我需要进一步处理吗?不能手动编辑每张照片。

    更新:按照建议,我对这个设备上的14张照片进行了pngcheck。它返回了2个有效值和12个无效值,错误为: zlib: inflate error = -3 (data error) 所有14个都失败,如上所述使用ImageIO。

    有此问题的图像可以在以下位置看到: https://tracweb-safecommunity.rhcloud.com/rest/monitoredProfile/106/testResult_8284.png

    1 回复  |  直到 9 年前
        1
  •  2
  •   Harald K    9 年前

    在我看来,特定设备(特定于供应商的操作系统构建?)生成断开的PNG。然而,似乎唯一缺少的是一些Zip/zlib数据完整性检查值,如果忽略数据完整性检测,则可以正确重建图像。

    出于某种原因,我的原始答案(如下)不适用于OP。因此,这里有一种仅使用ImageIO的替代(更详细)方法:

    InputStream input = new ByteArrayInputStream(form.getImageFileData());
    Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
    
    if (!readers.hasNext()) {
        // TODO: Handle, return null or throw exception, whatever is more appropriate
    }
    
    ImageReader reader = readers.next();
    reader.setInput(input);
    
    try {
        ImageReadParam param = reader.getDefaultReadParam();
        int imageNo = 0;
    
        int width = reader.getWidth(imageNo);
        int height = reader.getHeight(imageNo);
    
        // If possible, create a destination image up front
        ImageTypeSpecifier type = reader.getRawImageType(imageNo);
        if (type != null) {
            param.setDestination(type.createBufferedImage(width, height));
        }
    
        // Decode into the destination
        BufferedImage image;
        try {
            image = reader.read(imageNo, param);
        }
        catch (IOException e) {
            if (e.getCause() instanceof ZipException && param.getDestination() != null) {
                // If we got here, the destination will contain a partial image
                // We'll use that.
                image = param.getDestination();
            }
            else {
                throw e;
            }
        }
    }
    finally {
        input.close();
    }
    

    由于 ZipException ,否则,图像看起来不错。


    下面是一个使用Java的可能解决方案。我已经在OSX上测试了它,它适用于我,使用Java 1.7.0_71和1.8.0_51(都是Oracle JRE),使用提供的测试文件。堆栈跟踪被打印到控制台,图像的最后一行丢失,否则,它看起来很好:

    byte[] data = form.getImageFileData();
    Image tmp = Toolkit.getDefaultToolkit().createImage(data);
    BufferedImage image = new BufferedImageFactory(tmp).getBufferedImage();
    

    这将比使用 ImageIO ,所以我建议您首先尝试使用 后台以流 如前所述,然后仅在失败时使用此代码作为后备 java.util.zip.ZipException 根本原因。

    PS:您可能可以将 Image BufferedImage 使用 MediaTracker 完全加载(或使用 ImageIcon hack),然后将结果绘制到 缓冲图像 如果你不介意失去一些精度,比如原始的颜色模型等等。


    这个 BufferedImageFactory 类是我的TwelveMonkeys ImageIO库的一部分,在BSD许可下可用,可以在 on GitHub .