66

I need to know the string path to a file on assets folder, because I'm using a map API that needs to receive a string path, and my maps must be stored on assets folder

This is the code i'm trying:

    MapView mapView = new MapView(this);
    mapView.setClickable(true);
    mapView.setBuiltInZoomControls(true);
    mapView.setMapFile("file:///android_asset/m1.map");
    setContentView(mapView);

Something is going wrong with "file:///android_asset/m1.map" because the map is not being loaded.

Which is the correct string path file to the file m1.map stored on my assets folder?

Thanks

EDIT for Dimitru: This code doesn't works, it fails on is.read(buffer); with IOException

        try {
            InputStream is = getAssets().open("m1.map");
            int size = is.available();
            byte[] buffer = new byte[size];
            is.read(buffer);
            is.close();
            text = new String(buffer);
        } catch (IOException e) {throw new RuntimeException(e);}
ppreetikaa
  • 1,149
  • 2
  • 15
  • 22
NullPointerException
  • 36,107
  • 79
  • 222
  • 382

4 Answers4

102

AFAIK the files in the assets directory don't get unpacked. Instead, they are read directly from the APK (ZIP) file.

So, you really can't make stuff that expects a file accept an asset 'file'.

Instead, you'll have to extract the asset and write it to a seperate file, like Dumitru suggests:

  File f = new File(getCacheDir()+"/m1.map");
  if (!f.exists()) try {

    InputStream is = getAssets().open("m1.map");
    int size = is.available();
    byte[] buffer = new byte[size];
    is.read(buffer);
    is.close();


    FileOutputStream fos = new FileOutputStream(f);
    fos.write(buffer);
    fos.close();
  } catch (Exception e) { throw new RuntimeException(e); }

  mapView.setMapFile(f.getPath());
Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Jacob Nordfalk
  • 3,533
  • 1
  • 21
  • 21
  • 4
    i get exception: java.io.FileNotFoundException: m1.map – NullPointerException Dec 12 '11 at 14:50
  • the exception is on the line InputStream is = getAssets().open("m1.map"); – NullPointerException Dec 12 '11 at 14:57
  • 1
    ok, solved, but now i got the same exception that with Dumitru answer, 12-12 15:06:41.452: DEBUG/asset(3760): Data exceeds UNCOMPRESS_DATA_MAX (3491923 vs 1048576) – NullPointerException Dec 12 '11 at 15:05
  • Android per default tries to compress the data, but there is a max file size and your file is too large to be uncompressed. To fake Android into not trying to compress your file, rename m1.map to a format which isnt compressed, like m1.mp3. See http://www.nutprof.com/2010/12/data-exceeds-uncompressdatamax.html – Jacob Nordfalk Dec 13 '11 at 08:23
  • 2
    Nice that is exactly correct, the FILES in the assets directory are compressed so you have to extract to a byte array, write to a temp file and load that file, THANKS! – Danuofr May 22 '13 at 21:06
  • 1
    Oh yeah you can use "file:///android_asset/FILENAME" for a url – Danuofr May 22 '13 at 21:09
16

You can use this method.

    public static File getRobotCacheFile(Context context) throws IOException {
        File cacheFile = new File(context.getCacheDir(), "robot.png");
        try {
            InputStream inputStream = context.getAssets().open("robot.png");
            try {
                FileOutputStream outputStream = new FileOutputStream(cacheFile);
                try {
                    byte[] buf = new byte[1024];
                    int len;
                    while ((len = inputStream.read(buf)) > 0) {
                        outputStream.write(buf, 0, len);
                    }
                } finally {
                    outputStream.close();
                }
            } finally {
                inputStream.close();
            }
        } catch (IOException e) {
            throw new IOException("Could not open robot png", e);
        }
        return cacheFile;
    }

You should never use InputStream.available() in such cases. It returns only bytes that are buffered. Method with .available() will never work with bigger files and will not work on some devices at all.

In Kotlin (;D):

@Throws(IOException::class)
fun getRobotCacheFile(context: Context): File = File(context.cacheDir, "robot.png")
    .also {
        it.outputStream().use { cache -> context.assets.open("robot.png").use { it.copyTo(cache) } }
    }
Jacek Marchwicki
  • 1,565
  • 15
  • 17
10

Have a look at the ReadAsset.java from API samples that come with the SDK.

       try {
        InputStream is = getAssets().open("read_asset.txt");

        // We guarantee that the available method returns the total
        // size of the asset...  of course, this does mean that a single
        // asset can't be more than 2 gigs.
        int size = is.available();

        // Read the entire asset into a local byte buffer.
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();

        // Convert the buffer into a string.
        String text = new String(buffer);

        // Finally stick the string into the text view.
        TextView tv = (TextView)findViewById(R.id.text);
        tv.setText(text);
    } catch (IOException e) {
        // Should never happen!
        throw new RuntimeException(e);
    }
Dumitru Hristov
  • 1,359
  • 1
  • 11
  • 29
5

Just to add on Jacek's perfect solution. If you're trying to do this in Kotlin, it wont work immediately. Instead, you'll want to use this:

@Throws(IOException::class)
fun getSplashVideo(context: Context): File {
    val cacheFile = File(context.cacheDir, "splash_video")
    try {
        val inputStream = context.assets.open("splash_video")
        val outputStream = FileOutputStream(cacheFile)
        try {
            inputStream.copyTo(outputStream)
        } finally {
            inputStream.close()
            outputStream.close()
        }
    } catch (e: IOException) {
        throw IOException("Could not open splash_video", e)
    }
    return cacheFile
}
CheesusCrust
  • 87
  • 1
  • 2