java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed
Asked Answered
J

14

61

i am programming a soundboard from android. the problem is that some sounds works, and some dont work. here is the traceback that i get for the sounds that doesnt work

05-31 13:23:04.227 18440 18603 W System.err: java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed
05-31 13:23:04.227 18440 18603 W System.err:    at android.content.res.AssetManager.openAssetFd(Native Method)
05-31 13:23:04.227 18440 18603 W System.err:    at android.content.res.AssetManager.openFd(AssetManager.java:331)
05-31 13:23:04.227 18440 18603 W System.err:    at com.phonegap.AudioPlayer.startPlaying(AudioPlayer.java:201)
05-31 13:23:04.227 18440 18603 W System.err:    at com.phonegap.AudioHandler.startPlayingAudio(AudioHandler.java:181)
05-31 13:23:04.235 18440 18603 W System.err:    at com.phonegap.AudioHandler.execute(AudioHandler.java:64)
05-31 13:23:04.235 18440 18603 W System.err:    at com.phonegap.api.PluginManager$1.run(PluginManager.java:86)
05-31 13:23:04.235 18440 18603 W System.err:    at java.lang.Thread.run(Thread.java:1096)

any ideas?

Jacquline answered 31/5, 2011 at 11:27 Comment(0)
L
45

There is a limitations on opening compressed files in the assets folder. This is because uncompressed files can be directly memory mapped into the processes virtual address space, therefore avoiding needing the same amount of memory again for decompression.

Dealing with Asset Compression in Android Apps discusses some techniques in dealing with compressed files. You can trick aapt into not compressing the file by using an extension that is not compressed (e.g. mp3) or you can manually add them to the apk without compression instead of getting aapt to do the work.

Luwian answered 31/5, 2011 at 11:48 Comment(5)
Finally, an answer to all of my problems. This is really poorly documented! +1Iaverne
The binary asset file I have is around 1.1MB. I renamed it to jpg/png etc so that the compression does not kick in. But when copied to the sdcard, it swells up to ~8MB. So somehow, AssetManager still thinks that it needs to be uncompressed. Does anyone know whats going on here?Kerbstone
After wasting a few hours, I finally managed to read the files off of raw instead of assets and they are not 'uncompressed' any more.Kerbstone
It'd be nice if there were a list of the extensions that aren't compressed :-PVegetarian
@Kerbstone would you mind pointing out how to copy a file from the raw directory? I've tried adding the noCompress option, for a concrete extension, and copying the file (bigger than 1MB) but nothing is working. I get the size of the APK in the copied file. Strange :SExperimentation
B
118

You can disable asset compression for certain extensions like so:

android {
    aaptOptions {
        noCompress "pdf"
    }
}

Source

Bozovich answered 27/10, 2015 at 3:50 Comment(5)
in my case there was a zip already in assets folder. So it compressed the file once again while compilation. I have put "zip" instead of "pdf" and the error gone.Federalist
You can verify which entries are compressed with this command: unzip -lv MyApplication.apk 'Stored' in the 'Method' column means the file is not compressed.Babel
You saved me from banging my head against the wall :D Thank you very much!Abbacy
Worked for me, i had an .xml file. Once i added it to noCompress all worked fine, thank youSneaking
Thanks a lot, man, this has been bugging me for weeks. For some reason I could not play ogg files anymore. I didn't touch my code or my phone, I only updated Android Studio. Glad to have found this answer, thanks again.Whale
Q
53

People working with Tensorflow Lite file running into this issue,

Add the following lines to your Gradle file (android/app/build.gradle) inside the android{} block.

aaptOptions {
    noCompress "tflite"
}
Quarry answered 29/7, 2019 at 7:57 Comment(1)
It is Ok on sample project. But when i compile aar, this error happens in aar mode in new project.Kerosene
L
45

There is a limitations on opening compressed files in the assets folder. This is because uncompressed files can be directly memory mapped into the processes virtual address space, therefore avoiding needing the same amount of memory again for decompression.

Dealing with Asset Compression in Android Apps discusses some techniques in dealing with compressed files. You can trick aapt into not compressing the file by using an extension that is not compressed (e.g. mp3) or you can manually add them to the apk without compression instead of getting aapt to do the work.

Luwian answered 31/5, 2011 at 11:48 Comment(5)
Finally, an answer to all of my problems. This is really poorly documented! +1Iaverne
The binary asset file I have is around 1.1MB. I renamed it to jpg/png etc so that the compression does not kick in. But when copied to the sdcard, it swells up to ~8MB. So somehow, AssetManager still thinks that it needs to be uncompressed. Does anyone know whats going on here?Kerbstone
After wasting a few hours, I finally managed to read the files off of raw instead of assets and they are not 'uncompressed' any more.Kerbstone
It'd be nice if there were a list of the extensions that aren't compressed :-PVegetarian
@Kerbstone would you mind pointing out how to copy a file from the raw directory? I've tried adding the noCompress option, for a concrete extension, and copying the file (bigger than 1MB) but nothing is working. I get the size of the APK in the copied file. Strange :SExperimentation
P
13

You should disable compression for that file. Simply add:

    aaptOptions {
       noCompress "your-file-name"
    }

To your app level build.gradle file inside android { }

Philbo answered 26/10, 2018 at 11:35 Comment(0)
K
5

This decidedly irritating situation comes about because when the .apk is built, some assets are compressed before storing them, whereas other are treated as already compressed (e.g. images, video) and are left alone. The latter group can be opened using openAssetFd, the former group can't - if you try, you get the "This file can not be opened as a file descriptor; it is probably compressed" error.

One option is to trick the build system into not compressing the assets (see the link in @nicstrong's answer), but this is fiddly. Better to try and work around the problem in a more predictable fashion.

The solution I cam up with uses the fact that while you can't open an AssetFileDescriptor for the asset, you can still open an InputStream. You can use this to copy the asset into the application's file cache, and then return a descriptor for that:

@Override
public AssetFileDescriptor openAssetFile(final Uri uri, final String mode) throws FileNotFoundException
{
    final String assetPath = uri.getLastPathSegment();  // or whatever

    try
    {
        final boolean canBeReadDirectlyFromAssets = ... // if your asset going to be compressed?
        if (canBeReadDirectlyFromAssets)
        {
            return getContext().getAssets().openFd(assetPath);
        }
        else
        {
            final File cacheFile = new File(getContext().getCacheDir(), assetPath);
            cacheFile.getParentFile().mkdirs();
            copyToCacheFile(assetPath, cacheFile);
            return new AssetFileDescriptor(ParcelFileDescriptor.open(cacheFile, MODE_READ_ONLY), 0, -1);
        }
    }
    catch (FileNotFoundException ex)
    {
        throw ex;
    }
    catch (IOException ex)
    {
        throw new FileNotFoundException(ex.getMessage());
    }
}

private void copyToCacheFile(final String assetPath, final File cacheFile) throws IOException
{
    final InputStream inputStream = getContext().getAssets().open(assetPath, ACCESS_BUFFER);
    try
    {
        final FileOutputStream fileOutputStream = new FileOutputStream(cacheFile, false);
        try
        {
            //using Guava IO lib to copy the streams, but could also do it manually
            ByteStreams.copy(inputStream, fileOutputStream); 
        }
        finally
        {
            fileOutputStream.close();
        }
    }
    finally
    {
        inputStream.close();
    }
}

This does mean that your app will leave cache files lying about, but that's fine. It also doesn't attempt to re-use existing cache files, which you may or may not care about.

Kalmia answered 19/6, 2014 at 4:19 Comment(0)
M
5

This exception can be thrown by calling:

final AssetFileDescriptor afd = activity.getAssets().openFd(path);

I fixed the problem by saving the file in res/raw directory instead of assets folder, then get the AssetFileDescriptor this way:

final AssetFileDescriptor afd = activity.getResources().openRawResourceFd(rawId);

Then the FileNotFoundException is gone, and the file is not compressed anymore.

Melanesian answered 5/9, 2018 at 7:29 Comment(0)
V
4

You should get this exception only if trying to open the FileDesriptor. For just reading the file you can go the way through the InputStream (AssetManager.open("filename.ext")). This worked for me.

If you need the file size in advance, you need the FileDescriptor (and therefore an uncompressed file) to call its getLength() method, otherwise you have to read the whole stream to determine its size.

Varga answered 22/7, 2013 at 13:22 Comment(0)
M
2

I have done a walk around, I use:

ParcelFileDescriptor mFileDescriptor = context.getAssets().openFd(file).getParcelFileDescriptor();

But that return: java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed.

Instead of this implementation I open the file directly using functions form ParcelFileDescriptor.

private void openRenderer(Context context,String fileName) throws IOException {  

File file=  FileUtils.fileFromAsset(context, fileName);
        ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(file,ParcelFileDescriptor.MODE_READ_WRITE); 

        mPdfRenderer = new PdfRenderer(parcelFileDescriptor);
    }`

    public class FileUtils {
    private FileUtils() {
    }

    public static File fileFromAsset(Context context, String assetName) throws IOException {
        File outFile = new File(context.getCacheDir(), assetName );
        copy(context.getAssets().open(assetName), outFile);

        return outFile;
    }

    public static void copy(InputStream inputStream, File output) throws IOException {
        FileOutputStream outputStream = null;

        try {
            outputStream = new FileOutputStream(output);
            boolean read = false;
            byte[] bytes = new byte[1024];

            int read1;
            while((read1 = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, read1);
            }
        } finally {
            try {
                if(inputStream != null) {
                    inputStream.close();
                }
            } finally {
                if(outputStream != null) {
                    outputStream.close();
                }

            }

        }

    }
}
Manns answered 15/7, 2015 at 8:20 Comment(0)
A
2

For kotlin dsl it would look like this:

    androidResources {
        noCompress.add("tflite")
    }
Affricate answered 12/8, 2023 at 10:22 Comment(0)
L
1

Just added the extension of my file like below in the build.gradle and solved my issue

android {
   aaptOptions {
      noCompress "tflite"
      noCompress "txt"
      noCompress "pdf"
   }
}
Lashonda answered 23/11, 2020 at 14:38 Comment(0)
E
0

If the file to be obtained from the assets folder is bigger than 1MB, then what has worked for me is to compress the file as a zip file, then unzip it before using it, and store it in the external storage uncompressed.

InputStream fileInputStream = getAssets().open("your_file.your_file_extension.zip");
unzipInputStream(fileInputStream, "your_folder_in_external_storage");

The unzipInputStream method I've used is this one:

public static void unzipInputStream(InputStream inputStream, String location)
{
    try {
        if ( !location.endsWith(File.separator) ) {
            location += File.separator;
        }
        File f = new File(location);
        if(!f.isDirectory()) {
            f.mkdirs();
        }
        ZipInputStream zin = new ZipInputStream(new BufferedInputStream(inputStream, BUFFER_SIZE));
        try {
            ZipEntry ze;
            while ((ze = zin.getNextEntry()) != null) {
                String path = location + ze.getName();
                File unzipFile = new File(path);

                if (ze.isDirectory()) {
                    if(!unzipFile.isDirectory()) {
                        unzipFile.mkdirs();
                    }
                } else {
                    createParentDirectoriesIfMissing(unzipFile);
                    unzipFile(zin, unzipFile);
                }
            }
        } finally {
            zin.close();
        }
    } catch (Exception e) {
        Log.e("", "Unzip exception", e);
    }
}

private static void createParentDirectoriesIfMissing(File unzipFile)
{
    File parentDir = unzipFile.getParentFile();
    if ( null != parentDir ) {
        if ( !parentDir.isDirectory() ) {
            parentDir.mkdirs();
        }
    }
}

private static void unzipFile(ZipInputStream zin, File unzipFile) throws IOException
{
    int size;
    byte[] buffer = new byte[BUFFER_SIZE];
    FileOutputStream out = new FileOutputStream(unzipFile, false);
    BufferedOutputStream fout = new BufferedOutputStream(out, BUFFER_SIZE);

    try {
        while ( (size = zin.read(buffer, 0, BUFFER_SIZE)) != -1 ) {
            fout.write(buffer, 0, size);
        }

        zin.closeEntry();
    } finally {
        fout.flush();
        fout.close();
    }
}
Experimentation answered 10/11, 2018 at 12:32 Comment(0)
O
0

I just ran into the same problem, because the gnome-sound-recorder creates OGG files, which I can't play using the MediaPlayer. So I converted them to MP3 with ffmpeg and it worked. So I guess this is the easiest way.

ffmpeg -i youroggfile yournewfile.mp3

What I also noticed is that it does still show up with a question mark in the resources and that when I access it with R.raw.yournewfile I do not write the ".mp3" extension in the code.

Odo answered 12/11, 2019 at 15:45 Comment(0)
A
0

I got the same issue, and I copy the file from res/raw to /data/data/your.app.pkg/cache folder, then everything go well :D

AssetFileDescriptor afd = null;
try {
    File cache = new File(getCacheDir(), "my_data.dat");
    if (!cache.exists()) {
        copyInputStreamToFile(getResources().openRawResource(R.raw.my_data), cache);
    }
    afd = new AssetFileDescriptor(ParcelFileDescriptor.open(cache, ParcelFileDescriptor.MODE_READ_ONLY), 0, AssetFileDescriptor.UNKNOWN_LENGTH);
} catch (Exception e) {
    e.printStackTrace();
}


private void copyInputStreamToFile(InputStream in, File file) {
    BufferedOutputStream bfos = null;

    try {
        bfos = new BufferedOutputStream(new FileOutputStream(file));
        byte[] buf = new byte[4096];

        int len;
        while ((len = in.read(buf)) != -1) {
            bfos.write(buf, 0, len);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (bfos != null) {
                bfos.close();
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Assured answered 2/9, 2020 at 12:17 Comment(0)
B
0

In my case, it caused by resource.arsc is compressed. Rebuilding with uncompressed resource.arsc solve the problem.

Barrybarrymore answered 30/12, 2020 at 4:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.