ExifInterface constructor throws IOExxception

rbing picture rbing · Oct 16, 2016 · Viewed 7.5k times · Source

I am having trouble figuring out an exception that is thrown by ExifInterface constructor when trying to initialize the exif interface instance with a file path.

UPDATED Please see the detailed code below as requested.

File download function

public void downloadAndSaveFile(String url, String directoryId, String fileName) {
    HttpURLConnection conn = null;
    try {
        Log.d(TAG, "DownloadFileTask url : " + url);
        conn = getGETConnection(url);
        conn.setRequestProperty("Accept", "application/json");
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("Authorization", "Bearer " + MY_AUTH_TOKEN);
        conn.connect();
        File file = new File(FileTools.getCacheFileLocation(fileName, directoryId));
        FileOutputStream fileOutput = new FileOutputStream(file);
        InputStream inputStream = (InputStream) conn.getInputStream();
        byte[] buffer = new byte[1024 * 1024];
        int bufferLength = 0;
        while ((bufferLength = inputStream.read(buffer)) > 0) {
            fileOutput.write(buffer, 0, bufferLength);
        }
        fileOutput.close();
        inputStream.close();
        Log.d(TAG, "Download successful. Downloaded File : " + file.getAbsolutePath());
        // Generate thumbnail and encrypt the file and thumbnail
        ***String thumbnailPath = FileTools.cacheThumbnail(file, file.getName(), directoryId);***
        if (thumbnailPath != null && !thumbnailPath.isEmpty()) {
            // Encrypt the file
            try {
                FileTools.SaveFileEncrypted(file, directoryId);
            } catch (Exception e) {
                e.printStackTrace();
                // Delete the file
                file.delete();
            }
        } else {
            // Delete the file
            file.delete();
        }
    } catch (Exception e) {
        Log.d(TAG, "Exception while downloading file");
        e.printStackTrace();
    } finally {
        if (conn != null) {
            conn.disconnect();
        }
    }
}

The FileTools.cacheThumbnail function

public static String cacheThumbnail(File file, String fileName, String chatId) {
    Log.d(TAG, "cacheThumbnail original File path : " + file.getAbsolutePath());
    Log.d(TAG, "cacheThumbnail original File exists : " + file.exists());
    Log.d(TAG, "cacheThumbnail original file size : " + file.length());
    String thumbName = String.format("thumb-%s.jpg", fileName);
    File fileThumb = new File(FileTools.getMediaCachePath(chatId), thumbName);
    ByteArrayOutputStream out = null;
    try {
        out = new ByteArrayOutputStream();
        BitmapFactory.Options options = new BitmapFactory.Options();
        Bitmap image = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(file.toURI().getPath(), options), 256, 256, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
        // Thumbnail generation for the image failed. It is a video file. Generate
        // thumbnail for the video file
        if (image == null) {
            MediaMetadataRetriever retriever = new MediaMetadataRetriever();
            try {
                retriever.setDataSource(FileTools.getFileInputStreamFromStorage(file).getFD());
                // Generate thumbnail from a frame that is 5% deep into the video
                String time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
                long timeInMillisec = Long.parseLong(time);
                long durationMicroSec = timeInMillisec * 1000;
                long thumbnailDepth = (long) (durationMicroSec * (0.15f));
                image = retriever.getFrameAtTime(thumbnailDepth, MediaMetadataRetriever.OPTION_CLOSEST);
                image = ThumbnailUtils.extractThumbnail(image, 256, 256, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
            } catch (IllegalArgumentException ex) {
                Log.e(TAG, ex.getMessage());
                image = null;
            } catch (RuntimeException ex) {
                Log.e(TAG, ex.getMessage());
                image = null;
            } catch (IOException e) {
                image = null;
                Log.e(TAG, e.getMessage());
            }
        }
        // Could not generate a thumbnail. Probably a corrupt/bad file
        if (image == null) {
            return null;
        }
        ***image = Utilities.orientBitmap(file.getAbsolutePath(), image);***
        image.compress(Bitmap.CompressFormat.JPEG, 80, out);
        out.close();
        FileTools.SaveFileEncrypted(fileThumb, out.toByteArray());
        return fileThumb.toURI().getPath();
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    } finally {
        try {
            if (out != null) {
                out.close();
            }
        } catch (Exception ignore) {
        }
    }
}

The orientBitmap and rotateBitmap functions function

public static Bitmap orientBitmap(String filePath, Bitmap bitmap) throws IOException {
    Log.d(TAG, "orientBitmap FilePath : " + filePath);
    File file = new File(filePath);
    Log.d(TAG, "orientBitmap File exists : " + file.exists());

    ExifInterface exifInterface = new ExifInterface(filePath);
    Log.d(TAG, "After exception");
    int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

    Log.d(TAG, "Thumb orientation : " + orientation);

    switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
            bitmap = rotateBitmap(bitmap, 90);
            break;
        case ExifInterface.ORIENTATION_ROTATE_180:
            bitmap = rotateBitmap(bitmap, 180);
            break;
        case ExifInterface.ORIENTATION_ROTATE_270:
            bitmap = rotateBitmap(bitmap, 270);
            break;
    }
    return bitmap;
}

public static Bitmap rotateBitmap(Bitmap source, float angle) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
}

I am updating the stack traces by posting stack trace for when trying to generate the thumbnail of a mp4 file and png file.

Stack trace for mp4 file.

D/DownloadFileClass: downloadAndSaveFile url : example.com/media/download/568405
D/DownloadFileClass: Download successful. Downloaded File : /data/user/0/com.mypackage.myapp/cache/userhash/79640/media/MP4_20161017_134641.mp4
D/FileTools: cacheThumbnail original File path : /data/user/0/com.mypackage.myapp/cache/userhash/79640/media/MP4_20161017_134641.mp4
D/FileTools: cacheThumbnail original File exists : true
D/FileTools: cacheThumbnail original file size : 6434816
D/skia: --- SkImageDecoder::Factory returned null
D/Utilities: orientBitmap FilePath : /data/user/0/com.mypackage.myapp/cache/userhash/79640/media/MP4_20161017_134641.mp4
D/Utilities: orientBitmap File exists : true
W/ExifInterface: Invalid image.
    java.io.IOException: Invalid marker: 0
    at android.media.ExifInterface.getJpegAttributes(ExifInterface.java:1600)
           at android.media.ExifInterface.loadAttributes(ExifInterface.java:1339)
           at android.media.ExifInterface.<init>(ExifInterface.java:1057)
           at com.mypackage.helpers.Utilities.orientBitmap(Utilities.java:85)
           at com.mypackage.fileio.FileTools.cacheThumbnail(FileTools.java:700)
           at com.mypackage.coreapi.DownloadFileClass$downloadAndSaveFile(DownloadFileClass.java:215)
           at com.mypackage.coreapi.DownloadFileClass$downloadAndSaveFile(DownloadFileClass.java:113)
           at android.os.AsyncTask$2.call(AsyncTask.java:295)
           at java.util.concurrent.FutureTask.run(FutureTask.java:237)
           at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
           at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
           at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
           at java.lang.Thread.run(Thread.java:818)
D/Utilities: After exception
D/Utilities: Thumb orientation : 0
D/FileTools: Save file to : /data/user/0/com.mypackage.myapp/cache/userhash/79640/media/MP4_20161017_134641.mp4
D/EncryptedFileTools: ......Final saved file path...... : /data/user/0/com.mypackage.myapp/cache/userhash/79640/media/encryptPlaceHolder

Stack trace for png file

D/DownloadFileClass: downloadAndSaveFile url : example.com/media/download/568406
D/DownloadFileClass: Download successful. Downloaded File : /data/user/0/com.mypackage.myapp/cache/userhash/79640/media/PNG_20161017_134748.png
D/FileTools: cacheThumbnail original File path : /data/user/0/com.mypackage.myapp/cache/userhash/79640/media/PNG_20161017_134748.png
D/FileTools: cacheThumbnail original File exists : true
D/FileTools: cacheThumbnail original file size : 92160
D/Utilities: orientBitmap FilePath : /data/user/0/com.mypackage.myapp/cache/userhash/79640/media/PNG_20161017_134748.png
D/Utilities: orientBitmap File exists : true
W/ExifInterface: Invalid image.
    java.io.IOException: Invalid marker: 89
           at android.media.ExifInterface.getJpegAttributes(ExifInterface.java:1600)
           at android.media.ExifInterface.loadAttributes(ExifInterface.java:1339)
           at android.media.ExifInterface.<init>(ExifInterface.java:1057)
           at com.mypackage.helpers.Utilities.orientBitmap(Utilities.java:85)
           at com.mypackage.fileio.FileTools.cacheThumbnail(FileTools.java:700)
           at com.mypackage.coreapi.DownloadFileClass$downloadAndSaveFile(DownloadFileClass.java:215)
           at com.mypackage.coreapi.DownloadFileClass$downloadAndSaveFile(DownloadFileClass.java:113)
           at android.os.AsyncTask$2.call(AsyncTask.java:295)
           at java.util.concurrent.FutureTask.run(FutureTask.java:237)
           at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
           at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
           at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
           at java.lang.Thread.run(Thread.java:818)
D/Utilities: After exception
D/Utilities: Thumb orientation : 0
D/FileTools: Save file to : /data/user/0/com.mypackage.myapp/cache/userhash/79640/media/PNG_20161017_134748.png
D/EncryptedFileTools: ......Final saved file path...... : /data/user/0/com.mypackage.myapp/cache/userhash/79640/media/encryptPlaceHolder

The confusing part is that I do not get this exception when I run the app on my Samsung Galaxy S4 with android 4.4.4 but I always get it when I run it on my Samsung Galaxy S5 with android 6.0.1. I also get this error on the emulator with Android 7.0. I am not sure how to address this issue.

Please see the attached permissions screen shot. Enabled Permissions

I have added the requested information. Please let me know if any more information is needed. I have not worked with ExifTags before so please bear with me. Thanks again.

Answer

rbing picture rbing · Oct 27, 2016

I found the issue with my code above. Apparently I was trying to read the EXIF tags directly from mp4 and png files. Upon searching a little more about EXIF tags I found that EXIF tags are only available with JPEG files. So the fix for my issue was to first create a JPEG image of the mp4/png file and then try reading the EXIF tags from this JPEF file. Doing so fixed the issue and I do not get the exception anymore.

Thanks everyone for directing me to the solution.