ImageIO无法处理单个用户androidphone上拍摄的照片

Photos taken on a single user's android phone cannot be processed by ImageIO

我有一个 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);
}

然后到 PNG 使用:

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

然后 outputStream 被发布 (multipart/form-data) 到我的网络服务器 (jboss)。在服务器上,jax-rs 和 MultipartForm 将其转换为 data[] 并发送以进行额外处理,开头为:

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

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

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

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

任何人都可以帮助我了解仅在这台设备上可能导致此问题的原因,或者建议我可以在服务器上做些什么来解决它并仍然生成我需要进一步处理的 BufferedImage?手动编辑每张照片不是一种选择。

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

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

对我来说,似乎特定设备(供应商特定 OS 版本?)生成损坏的 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 的可能解决方法。我已经在 OS X 上测试过它,它对我有用,同时使用 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 慢一点,所以我建议您先像以前一样尝试使用 ImageIO,然后仅在失败并显示 [=15] 时将此代码用作后备=] 根本原因。

PS:您可以使用 MediaTrackerImage 转换为 BufferedImage 以完全加载它(或使用 ImageIcon hack),然后将结果也绘制到 BufferedImage,如果你不介意失去一些精度,比如原始颜色模型等


BufferedImageFactory class 是我的 TwelveMonkeys ImageIO 库的一部分,在 BSD 许可下可用,可以在 on GitHub 上找到。