How To Get File In Assets From Android NDK

45,756

Solution 1

You can read the image from an asset with AAssetManager_open & AAsset_read, but since asset lies in apk you can't get a file path name for it - it is also compressed there. You can save the data to a file and read from that file later or you can directly process the chunk you got from your asset file if OSG allows.

From here:

AAssetDir* assetDir = AAssetManager_openDir(mgr, "");
const char* filename = (const char*)NULL;
while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) {
    AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);
    char buf[BUFSIZ];
    int nb_read = 0;
    FILE* out = fopen(filename, "w");
    while ((nb_read = AAsset_read(asset, buf, BUFSIZ)) > 0)
        fwrite(buf, nb_read, 1, out);
    fclose(out);
    AAsset_close(asset);
}
AAssetDir_close(assetDir);

Solution 2

One more thing worth mentioning is that android puts restriction on asset size that can be read at time, to 1Mb. In case your file is bigger than that, you have to split it into chunks. Here is my working solution which loads file in chunks to vector of chars:

AAssetManager * mgr = app->activity->assetManager; 
AAssetDir* assetDir = AAssetManager_openDir(mgr, "");

const char* filename;
std::vector<char> buffer;

while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) 
{
    //search for desired file
    if(!strcmp(filename, /* searched file name */)) 
    {
        AAsset *asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);

        //holds size of searched file
        off64_t length = AAsset_getLength64(asset);
        //keeps track of remaining bytes to read
        off64_t remaining = AAsset_getRemainingLength64(asset);
        size_t Mb = 1000 *1024; // 1Mb is maximum chunk size for compressed assets
        size_t currChunk;
        buffer.reserve(length);

        //while we have still some data to read
        while (remaining != 0) 
        {
            //set proper size for our next chunk
            if(remaining >= Mb)
            {
                currChunk = Mb;
            }
            else
            {
                currChunk = remaining;
            }
            char chunk[currChunk];

            //read data chunk
            if(AAsset_read(asset, chunk, currChunk) > 0) // returns less than 0 on error
            {
                //and append it to our vector
                buffer.insert(buffer.end(),chunk, chunk + currChunk);
                remaining = AAsset_getRemainingLength64(asset);
            }
        }
        AAsset_close(asset);
    }

}
Share:
45,756
Dyn0myte
Author by

Dyn0myte

Updated on July 25, 2022

Comments

  • Dyn0myte
    Dyn0myte almost 2 years

    I'm trying to access an image file in the assets folder from the native side. Now I can successfully search through the assets folder and its subdirectories locating the particular file that I am looking for:

    AAssetDir* assetDir = AAssetManager_openDir(asset_manager, "images");
    const char* filename;
    while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL)
    {
       __android_log_print(ANDROID_LOG_DEBUG, "Debug", filename);
    }
    
    AAssetDir_close(assetDir);
    

    I am also using OSG. I set the image as a texture to my shader with the following code:

    texture->setImage(osgDB::readImageFile(fileNameWithPath));
    

    But that file name must include the absolute path as well since it is not located in the same directory. However I found out that I cannot get the absolute path for the assets directory, being that it is compressed along with the APK.

    Being that I can't access the absolute path, what other way could I use to get the image? Also to note, I do want to keep the files in the asset folder as appose to the sd card.

    • fnc12
      fnc12 almost 8 years
      I cannot understand - how to access asset_manager variable in c++ ndk code?
    • kaiser
      kaiser about 6 years
      android_app->activity->asset_manager
  • Dyn0myte
    Dyn0myte over 11 years
    Thanks for the fast response. I will try this out and give you an update when I can.
  • Dyn0myte
    Dyn0myte over 11 years
    Sorry for the late update (been a busy week). This will work fine. Is this how other apps normally access their files that are not in the SD card?
  • auselen
    auselen over 11 years
    @Robert no idea about other apps, but this looks ok.
  • user2591935
    user2591935 over 7 years
    Note that with this method you will extract the content of your APK in your disk. Rooted users will be able to access your application's assets so be careful. Doesn't osg have a method to read image from data ? You should load the data with AssetManager and load image from data.
  • Dan
    Dan about 7 years
    Note also, 1 MB might not be optimal for the architecture. Smaller chunk sizes like 512 KB may be more efficient to read if they fit entirely into the cache.
  • user673679
    user673679 over 6 years
    If you're reading the whole file, why not use AASSET_MODE_BUFFER and AAsset_getBuffer() like this: stackoverflow.com/a/33957074/673679
  • truthadjustr
    truthadjustr over 4 years
    The concept of reading the whole file is naive. Chunking, is always the best. But I agree of the AASSET_MODE_BUFFER though.
  • Alex Cohn
    Alex Cohn about 4 years
    @typelogic the platform uses StreamingZipInflater with chunk size (as of today's master) of 64Kbyte. This size is an implementation detail and may change for different Android version or on a different device (the OEM has full power to tune their constants to better fit their hardware). If you use AASSET_MODE_BUFFER, getBuffer() will be better than any chunks, because under the hood, the platform loads everything in one shot.
  • jiwopene
    jiwopene almost 4 years
    @user2591935, assets are accessible for all apps.
  • seb
    seb over 3 years
    My implementation is similar. However with devices with newer android version (e.g. 10+) my solution is not working properly. I will switch to AASSET_MODE_BUFFER for now. Has anybody else such problem on android 10+
  • Aaron Lee
    Aaron Lee about 3 years
    But how can I get 'mgr'