How to copy files from 'assets' folder to sdcard?
Asked Answered
A

23

269

I have a few files in the assets folder. I need to copy all of them to a folder say /sdcard/folder. I want to do this from within a thread. How do I do it?

Atonsah answered 15/12, 2010 at 7:2 Comment(2)
Are you Looking For This #4447977Fortnightly
Before you copy/paste one of the (great!) solutions below, consider using this library to do it in one line of code: https://mcmap.net/q/108630/-how-to-copy-files-from-39-assets-39-folder-to-sdcardRuff
A
361

If anyone else is having the same problem, this is how I did it

private void copyAssets() {
    AssetManager assetManager = getAssets();
    String[] files = null;
    try {
        files = assetManager.list("");
    } catch (IOException e) {
        Log.e("tag", "Failed to get asset file list.", e);
    }
    if (files != null) for (String filename : files) {
        InputStream in = null;
        OutputStream out = null;
        try {
          in = assetManager.open(filename);
          File outFile = new File(getExternalFilesDir(null), filename);
          out = new FileOutputStream(outFile);
          copyFile(in, out);
        } catch(IOException e) {
            Log.e("tag", "Failed to copy asset file: " + filename, e);
        }     
        finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    // NOOP
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    // NOOP
                }
            }
        }  
    }
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[1024];
    int read;
    while((read = in.read(buffer)) != -1){
      out.write(buffer, 0, read);
    }
}

Reference : Move file using Java

Atonsah answered 25/12, 2010 at 12:43 Comment(18)
to write files in the sdcard you have to give the permission on the manifest e.g. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />Schoenburg
why this doesnt work for me. I gave the permission in manifest file. I have a folder under assets like assets/myfolder/files I do assetManager.list("myfolder"); it gets the file names as I print them out but the code does not copy the files to sdcard. it only creates the folder there.. whats wrong?Whimsy
I would also not rely on sdcard being located at /sdcard, but retrieve the path with Environment.getExternalStorageDirectory()Tailpiece
You need to add a ((FileOutputStream)out).getFD().sync() developer.android.com/reference/java/io/…Etty
Should i use: 16* 1024 (16kb) I tend to opt for 16K or 32K as a good balance between memory usage and performance.Bluebird
@rciovati got this runtime error Failed to copy asset file: myfile.txt java.io.FileNotFoundException: myfile.txt at android.content.res.AssetManager.openAsset(Native Method)Coarctate
For me this code works only if I add this: in = assetManager.open("images-wall/"+filename); where "images-wall" is my folder inside assetsSingletree
this only works for flat assets folders without subfolders. the solution by Yoram Cohen handles these "special" cases in a better way.Piquet
Why ain't you doing out.flush() before close()?Characterization
This did work on Nexus 4 Lollipop but on Samsung SII IceCreamSandwich 4.0.3 i'm getting this error on one file 01-03 13:48:03.217: E/szipinf(19334): Error reading asset data 01-03 13:48:03.217: E/szipinf(19334): Unable to access asset data: -1Characterization
Any one else getting this error: This is real strange i have noticed that my file is getting write correctly regardless of exception so i just ignored it :)Characterization
If you need to copy the files from a subfolder (i.e. assets/MyFolder), then you have to replace the following lines: files=assetManager.list("MyFolder"); in=assetManager.open("MyFolder/"+filename);Geriatrics
I would check if the file already exists in sdcard before trying to copy again. Otherwise, it will try to copy the files everytime the app is launched: if (!(outFile.exists()))....Geriatrics
I added the two suggestions (i.e. copy files from a subfolder such as "assets/MyFolder" and checking if the file already exists in sdcard before copying again) in my answer below.Geriatrics
Why throw it like so (throws IOException) when you can try{..}catch(){..} it inside the block?Premises
can we do vise-versa , paste a file from sdcard or internal storage to apps asset folder.??Inseparable
@AnkushShrivastava did u found any answer for thatJurassic
@AnkushShrivastava https://stackoverflow.com/questions/3760626/how-to-write-files-to-assets-folder-or-raw-folder-in-android they are saying its impossible as assets are encrypted and compiledJurassic
S
67

Based on your solution, I did something of my own to allow subfolders. Someone might find this helpful:

...

copyFileOrDir("myrootdir");

...

private void copyFileOrDir(String path) {
    AssetManager assetManager = this.getAssets();
    String assets[] = null;
    try {
        assets = assetManager.list(path);
        if (assets.length == 0) {
            copyFile(path);
        } else {
            String fullPath = "/data/data/" + this.getPackageName() + "/" + path;
            File dir = new File(fullPath);
            if (!dir.exists())
                dir.mkdir();
            for (int i = 0; i < assets.length; ++i) {
                copyFileOrDir(path + "/" + assets[i]);
            }
        }
    } catch (IOException ex) {
        Log.e("tag", "I/O Exception", ex);
    }
}

private void copyFile(String filename) {
    AssetManager assetManager = this.getAssets();

    InputStream in = null;
    OutputStream out = null;
    try {
        in = assetManager.open(filename);
        String newFileName = "/data/data/" + this.getPackageName() + "/" + filename;
        out = new FileOutputStream(newFileName);

        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
    } catch (Exception e) {
        Log.e("tag", e.getMessage());
    }

}
Sexpartite answered 20/9, 2011 at 13:52 Comment(6)
assetManager.list(path) may be slow on device, to create assets paths list beforehand this snippet may be used from assets dir: find . -name "*" -type f -exec ls -l {} \; | awk '{print substr($9,3)}' >> assets.listGiant
Nice solution! The only required fix is to trim leading separators at the beginning of copyFileOrDir(): path= path.startsWith("/") ? path.substring(1) : path;Quarrier
This stackoverflow's on certain devices eg: S5Seigneur
Don't the streams need to be closed when an Exception happens?Scree
replace "/data/data/" + this.getPackageName() with this.getFilesDir().getAbsolutePath()Chalybeate
... and close streams in finally block ))Olshausen
W
50

The solution above did not work due to some errors:

  • directory creation did not work
  • assets returned by Android contain also three folders: images, sounds and webkit
  • Added way to deal with large files: Add extension .mp3 to the file in the assets folder in your project and during copy the target file will be without the .mp3 extension

Here is the code (I left the Log statements but you can drop them now):

final static String TARGET_BASE_PATH = "/sdcard/appname/voices/";

private void copyFilesToSdCard() {
    copyFileOrDir(""); // copy all files in assets folder in my project
}

private void copyFileOrDir(String path) {
    AssetManager assetManager = this.getAssets();
    String assets[] = null;
    try {
        Log.i("tag", "copyFileOrDir() "+path);
        assets = assetManager.list(path);
        if (assets.length == 0) {
            copyFile(path);
        } else {
            String fullPath =  TARGET_BASE_PATH + path;
            Log.i("tag", "path="+fullPath);
            File dir = new File(fullPath);
            if (!dir.exists() && !path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit"))
                if (!dir.mkdirs())
                    Log.i("tag", "could not create dir "+fullPath);
            for (int i = 0; i < assets.length; ++i) {
                String p;
                if (path.equals(""))
                    p = "";
                else 
                    p = path + "/";

                if (!path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit"))
                    copyFileOrDir( p + assets[i]);
            }
        }
    } catch (IOException ex) {
        Log.e("tag", "I/O Exception", ex);
    }
}

private void copyFile(String filename) {
    AssetManager assetManager = this.getAssets();

    InputStream in = null;
    OutputStream out = null;
    String newFileName = null;
    try {
        Log.i("tag", "copyFile() "+filename);
        in = assetManager.open(filename);
        if (filename.endsWith(".jpg")) // extension was added to avoid compression on APK file
            newFileName = TARGET_BASE_PATH + filename.substring(0, filename.length()-4);
        else
            newFileName = TARGET_BASE_PATH + filename;
        out = new FileOutputStream(newFileName);

        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
    } catch (Exception e) {
        Log.e("tag", "Exception in copyFile() of "+newFileName);
        Log.e("tag", "Exception in copyFile() "+e.toString());
    }

}

EDIT: Corrected a misplaced ";" that was throwing a systematic "could not create dir" error.

Weis answered 3/12, 2011 at 6:40 Comment(3)
this must became the solution!Catchpenny
NOTE: Log.i("tag", "could not create dir "+fullPath); always happens as ; is misplaced on the if.Meyeroff
awsome way! Thank so much! But why you check jpg file?Popularity
S
33

I know this has been answered but I have a slightly more elegant way to copy from asset directory to a file on the sdcard. It requires no "for" loop but instead uses File Streams and Channels to do the work.

(Note) If using any type of compressed file, APK, PDF, ... you may want to rename the file extension before inserting into asset and then rename once you copy it to SDcard)

AssetManager am = context.getAssets();
AssetFileDescriptor afd = null;
try {
    afd = am.openFd( "MyFile.dat");

    // Create new file to copy into.
    File file = new File(Environment.getExternalStorageDirectory() + java.io.File.separator + "NewFile.dat");
    file.createNewFile();

    copyFdToFile(afd.getFileDescriptor(), file);

} catch (IOException e) {
    e.printStackTrace();
}

A way to copy a file without having to loop through it.

public static void copyFdToFile(FileDescriptor src, File dst) throws IOException {
    FileChannel inChannel = new FileInputStream(src).getChannel();
    FileChannel outChannel = new FileOutputStream(dst).getChannel();
    try {
        inChannel.transferTo(0, inChannel.size(), outChannel);
    } finally {
        if (inChannel != null)
            inChannel.close();
        if (outChannel != null)
            outChannel.close();
    }
}
Silvan answered 26/6, 2012 at 17:38 Comment(16)
Liked this over the other solutions, little bit neater. Slight modification on mine that includes creating missing fileFolders. cheers!Hefter
Thank you I did this cause I ran into issues with looping through files if they were binary data. This copies the file correctly no matter what type it is.Silvan
It looked like it was working, but when copying APK files it seemed to copy it's self instead of the apk. I had to use one of the other solutions in the end.Hefter
This wouldnt work past the file descriptor for me, This file can not be opened as a file descriptor; it is probably compressed -- it is a pdf file. Know how to fix that?Marlowe
@Garreh Getting the same error. Looks like the only solution is to fiddle with the APK so the files do not get compressed. See this: https://mcmap.net/q/110774/-java-io-filenotfoundexception-this-file-can-not-be-opened-as-a-file-descriptor-it-is-probably-compressedDolley
My solution was to change the extension to .mp3, then remove it once copied.Marlowe
Never really tried it with compressed files, that is nice to know, thanks!Silvan
Eclipse says: Resource leak: '<unassigned Closeable value>' is never closedSeigneur
I didn't have all the code in there, you have to probably close the fileSilvan
I can't get this to work with compressed files on Android 5.0 wih renaming extension to mp3. Looks like that thrick is closed now.Orta
This assumes that inChannel.size() returns the size of the size of the file. It makes no such guarantee. I'm getting 2.5 MiB for 2 files that are 450 KiB each.Af
I've just found that AssetFileDescriptor.getLength() will return the correct filesize.Af
In addition to the above, the asset may not start at location 0 in the file descriptor. AssetFileDescriptor.getStartOffset() will return the starting offset.Af
Did not work, I get simillar 588kb files with some garbage inside. Solution copyAssets() works wellRuffle
Copied Image. But image is corrupted after copying.Handfasting
java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed Getting ErrorTshombe
C
11

This would be concise way in Kotlin.

    fun AssetManager.copyRecursively(assetPath: String, targetFile: File) {
        val list = list(assetPath)
        if (list.isEmpty()) { // assetPath is file
            open(assetPath).use { input ->
                FileOutputStream(targetFile.absolutePath).use { output ->
                    input.copyTo(output)
                    output.flush()
                }
            }

        } else { // assetPath is folder
            targetFile.delete()
            targetFile.mkdir()

            list.forEach {
                copyRecursively("$assetPath/$it", File(targetFile, it))
            }
        }
    }
Clarissa answered 15/1, 2019 at 5:39 Comment(2)
list(assetPath)?.let { ... }, actually. It's Nullable.Goingover
When using FileOutputStream, flush() is useless, because the implemention of flush() in OutputStream is empty.Hunterhunting
P
6

try out this it is much simpler ,this will help u:

// Open your local db as the input stream
    InputStream myInput = _context.getAssets().open(YOUR FILE NAME);

    // Path to the just created empty db
    String outFileName =SDCARD PATH + YOUR FILE NAME;

    // Open the empty db as the output stream
    OutputStream myOutput = new FileOutputStream(outFileName);

    // transfer bytes from the inputfile to the outputfile
    byte[] buffer = new byte[1024];
    int length;
    while ((length = myInput.read(buffer)) > 0) {
        myOutput.write(buffer, 0, length);
    }
    // Close the streams
    myOutput.flush();
    myOutput.close();
    myInput.close();
Pastoral answered 11/1, 2013 at 4:38 Comment(0)
P
4

Here is a cleaned up version for current Android devices, functional method design so that you can copy it to an AssetsHelper class e.g ;)

/**
 * 
 * Info: prior to Android 2.3, any compressed asset file with an
 * uncompressed size of over 1 MB cannot be read from the APK. So this
 * should only be used if the device has android 2.3 or later running!
 * 
 * @param c
 * @param targetFolder
 *            e.g. {@link Environment#getExternalStorageDirectory()}
 * @throws Exception
 */
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public static boolean copyAssets(AssetManager assetManager,
        File targetFolder) throws Exception {
    Log.i(LOG_TAG, "Copying files from assets to folder " + targetFolder);
    return copyAssets(assetManager, "", targetFolder);
}

/**
 * The files will be copied at the location targetFolder+path so if you
 * enter path="abc" and targetfolder="sdcard" the files will be located in
 * "sdcard/abc"
 * 
 * @param assetManager
 * @param path
 * @param targetFolder
 * @return
 * @throws Exception
 */
public static boolean copyAssets(AssetManager assetManager, String path,
        File targetFolder) throws Exception {
    Log.i(LOG_TAG, "Copying " + path + " to " + targetFolder);
    String sources[] = assetManager.list(path);
    if (sources.length == 0) { // its not a folder, so its a file:
        copyAssetFileToFolder(assetManager, path, targetFolder);
    } else { // its a folder:
        if (path.startsWith("images") || path.startsWith("sounds")
                || path.startsWith("webkit")) {
            Log.i(LOG_TAG, "  > Skipping " + path);
            return false;
        }
        File targetDir = new File(targetFolder, path);
        targetDir.mkdirs();
        for (String source : sources) {
            String fullSourcePath = path.equals("") ? source : (path
                    + File.separator + source);
            copyAssets(assetManager, fullSourcePath, targetFolder);
        }
    }
    return true;
}

private static void copyAssetFileToFolder(AssetManager assetManager,
        String fullAssetPath, File targetBasePath) throws IOException {
    InputStream in = assetManager.open(fullAssetPath);
    OutputStream out = new FileOutputStream(new File(targetBasePath,
            fullAssetPath));
    byte[] buffer = new byte[16 * 1024];
    int read;
    while ((read = in.read(buffer)) != -1) {
        out.write(buffer, 0, read);
    }
    in.close();
    out.flush();
    out.close();
}
Piquet answered 2/12, 2014 at 13:16 Comment(0)
A
4

Modified this SO answer by @DannyA

private void copyAssets(String path, String outPath) {
    AssetManager assetManager = this.getAssets();
    String assets[];
    try {
        assets = assetManager.list(path);
        if (assets.length == 0) {
            copyFile(path, outPath);
        } else {
            String fullPath = outPath + "/" + path;
            File dir = new File(fullPath);
            if (!dir.exists())
                if (!dir.mkdir()) Log.e(TAG, "No create external directory: " + dir );
            for (String asset : assets) {
                copyAssets(path + "/" + asset, outPath);
            }
        }
    } catch (IOException ex) {
        Log.e(TAG, "I/O Exception", ex);
    }
}

private void copyFile(String filename, String outPath) {
    AssetManager assetManager = this.getAssets();

    InputStream in;
    OutputStream out;
    try {
        in = assetManager.open(filename);
        String newFileName = outPath + "/" + filename;
        out = new FileOutputStream(newFileName);

        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();
        out.flush();
        out.close();
    } catch (Exception e) {
        Log.e(TAG, e.getMessage());
    }

}

Preparations

in src/main/assets add folder with name fold

Usage

File outDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString());
copyAssets("fold",outDir.toString());

In to the external directory find all files and directories that are within the fold assets

Antinucleon answered 15/8, 2016 at 10:49 Comment(0)
P
3

Copy all files and directories from assets to your folder!

for copying better use apache commons io

public void doCopyAssets() throws IOException {
    File externalFilesDir = context.getExternalFilesDir(null);

    doCopy("", externalFilesDir.getPath());

}

//THIS IS MAIN METHOD FOR COPY

private void doCopy(String dirName, String outPath) throws IOException {

    String[] srcFiles = assets.list(dirName);//for directory
    for (String srcFileName : srcFiles) {
        String outFileName = outPath + File.separator + srcFileName;
        String inFileName = dirName + File.separator + srcFileName;
        if (dirName.equals("")) {// for first time
            inFileName = srcFileName;
        }
        try {
            InputStream inputStream = assets.open(inFileName);
            copyAndClose(inputStream, new FileOutputStream(outFileName));
        } catch (IOException e) {//if directory fails exception
            new File(outFileName).mkdir();
            doCopy(inFileName, outFileName);
        }

    }
}

public static void closeQuietly(AutoCloseable autoCloseable) {
    try {
        if(autoCloseable != null) {
            autoCloseable.close();
        }
    } catch(IOException ioe) {
        //skip
    }
}

public static void copyAndClose(InputStream input, OutputStream output) throws IOException {
    copy(input, output);
    closeQuietly(input);
    closeQuietly(output);
}

public static void copy(InputStream input, OutputStream output) throws IOException {
    byte[] buffer = new byte[1024];
    int n = 0;
    while(-1 != (n = input.read(buffer))) {
        output.write(buffer, 0, n);
    }
}
Photoelasticity answered 2/11, 2013 at 19:16 Comment(0)
G
3

Based on Rohith Nandakumar's solution, I did something of my own to copy files from a subfolder of assets (i.e. "assets/MyFolder"). Also, I'm checking if the file already exists in sdcard before trying to copy again.

private void copyAssets() {
    AssetManager assetManager = getAssets();
    String[] files = null;
    try {
        files = assetManager.list("MyFolder");
    } catch (IOException e) {
        Log.e("tag", "Failed to get asset file list.", e);
    }
    if (files != null) for (String filename : files) {
        InputStream in = null;
        OutputStream out = null;
        try {
          in = assetManager.open("MyFolder/"+filename);
          File outFile = new File(getExternalFilesDir(null), filename);
          if (!(outFile.exists())) {// File does not exist...
                out = new FileOutputStream(outFile);
                copyFile(in, out);
          }
        } catch(IOException e) {
            Log.e("tag", "Failed to copy asset file: " + filename, e);
        }     
        finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    // NOOP
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    // NOOP
                }
            }
        }  
    }
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[1024];
    int read;
    while((read = in.read(buffer)) != -1){
      out.write(buffer, 0, read);
    }
}
Geriatrics answered 16/8, 2018 at 16:5 Comment(0)
P
3

You can do it in few steps using Kotlin, Here I am copying only few files instead of all from asstes to my apps files directory.

private fun copyRelatedAssets() {
    val assets = arrayOf("myhome.html", "support.css", "myscript.js", "style.css")
    assets.forEach {
        val inputStream = requireContext().assets.open(it)
        val nameSplit = it.split(".")
        val name = nameSplit[0]
        val extension = nameSplit[1]
        val path = inputStream.getFilePath(requireContext().filesDir, name, extension)
        Log.v(TAG, path)
    }
}

And here is the extension function,

fun InputStream.getFilePath(dir: File, name: String, extension: String): String {
    val file = File(dir, "$name.$extension")
    val outputStream = FileOutputStream(file)
    this.copyTo(outputStream, 4096)
    return file.absolutePath
}

LOGCAT

/data/user/0/com.***.***/files/myhome.html
/data/user/0/com.***.***/files/support.css
/data/user/0/com.***.***/files/myscript.js
/data/user/0/com.***.***/files/style.css
Pathic answered 9/9, 2020 at 12:24 Comment(0)
E
2

Based on Yoram Cohen answer, here is a version that supports non static target directory.

Invoque with copyFileOrDir(getDataDir(), "") to write to internal app storage folder /data/data/pkg_name/

  • Supports subfolders.
  • Supports custom and non-static target directory
  • Avoids copying "images" etc fake asset folders like

    private void copyFileOrDir(String TARGET_BASE_PATH, String path) {
    AssetManager assetManager = this.getAssets();
    String assets[] = null;
    try {
        Log.i("tag", "copyFileOrDir() "+path);
        assets = assetManager.list(path);
        if (assets.length == 0) {
            copyFile(TARGET_BASE_PATH, path);
        } else {
            String fullPath =  TARGET_BASE_PATH + "/" + path;
            Log.i("tag", "path="+fullPath);
            File dir = new File(fullPath);
            if (!dir.exists() && !path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit"))
                if (!dir.mkdirs())
                    Log.i("tag", "could not create dir "+fullPath);
            for (int i = 0; i < assets.length; ++i) {
                String p;
                if (path.equals(""))
                    p = "";
                else 
                    p = path + "/";
    
                if (!path.startsWith("images") && !path.startsWith("sounds") && !path.startsWith("webkit"))
                    copyFileOrDir(TARGET_BASE_PATH, p + assets[i]);
            }
        }
    } catch (IOException ex) {
        Log.e("tag", "I/O Exception", ex);
    }
    }
    
    private void copyFile(String TARGET_BASE_PATH, String filename) {
    AssetManager assetManager = this.getAssets();
    
    InputStream in = null;
    OutputStream out = null;
    String newFileName = null;
    try {
        Log.i("tag", "copyFile() "+filename);
        in = assetManager.open(filename);
        if (filename.endsWith(".jpg")) // extension was added to avoid compression on APK file
            newFileName = TARGET_BASE_PATH + "/" + filename.substring(0, filename.length()-4);
        else
            newFileName = TARGET_BASE_PATH + "/" + filename;
        out = new FileOutputStream(newFileName);
    
        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
    } catch (Exception e) {
        Log.e("tag", "Exception in copyFile() of "+newFileName);
        Log.e("tag", "Exception in copyFile() "+e.toString());
    }
    
    }
    
Elysha answered 29/3, 2014 at 16:24 Comment(0)
R
2

Using some of the concepts in the answers to this question, I wrote up a class called AssetCopier to make copying /assets/ simple. It's available on github and can be accessed with jitpack.io:

new AssetCopier(MainActivity.this)
        .withFileScanning()
        .copy("tocopy", destDir);

See https://github.com/flipagram/android-assetcopier for more details.

Ruff answered 1/2, 2017 at 1:37 Comment(0)
A
2

There are essentially two ways to do this.

First, you can use AssetManager.open and, as described by Rohith Nandakumar and iterate over the inputstream.

Second, you can use AssetManager.openFd, which allows you to use a FileChannel (which has the transferTo and transferFrom methods), so you don't have to loop over the input stream yourself.

I will describe the openFd method here.

Compression

First you need to ensure that the file is stored uncompressed. The packaging system may choose to compress any file with an extension that is not marked as noCompress, and compressed files cannot be memory mapped, so you will have to rely on AssetManager.open in that case.

You can add a '.mp3' extension to your file to stop it from being compressed, but the proper solution is to modify your app/build.gradle file and add the following lines (to disable compression of PDF files)

aaptOptions {
    noCompress 'pdf'
}

File packing

Note that the packager can still pack multiple files into one, so you can't just read the whole file the AssetManager gives you. You need to to ask the AssetFileDescriptor which parts you need.

Finding the correct part of the packed file

Once you've ensured your file is stored uncompressed, you can use the AssetManager.openFd method to obtain an AssetFileDescriptor, which can be used to obtain a FileInputStream (unlike AssetManager.open, which returns an InputStream) that contains a FileChannel. It also contains the starting offset (getStartOffset) and size (getLength), which you need to obtain the correct part of the file.

Implementation

An example implementation is given below:

private void copyFileFromAssets(String in_filename, File out_file){
    Log.d("copyFileFromAssets", "Copying file '"+in_filename+"' to '"+out_file.toString()+"'");
    AssetManager assetManager = getApplicationContext().getAssets();
    FileChannel in_chan = null, out_chan = null;
    try {
        AssetFileDescriptor in_afd = assetManager.openFd(in_filename);
        FileInputStream in_stream = in_afd.createInputStream();
        in_chan = in_stream.getChannel();
        Log.d("copyFileFromAssets", "Asset space in file: start = "+in_afd.getStartOffset()+", length = "+in_afd.getLength());
        FileOutputStream out_stream = new FileOutputStream(out_file);
        out_chan = out_stream.getChannel();
        in_chan.transferTo(in_afd.getStartOffset(), in_afd.getLength(), out_chan);
    } catch (IOException ioe){
        Log.w("copyFileFromAssets", "Failed to copy file '"+in_filename+"' to external storage:"+ioe.toString());
    } finally {
        try {
            if (in_chan != null) {
                in_chan.close();
            }
            if (out_chan != null) {
                out_chan.close();
            }
        } catch (IOException ioe){}
    }
}

This answer is based on JPM's answer.

Af answered 12/4, 2017 at 13:1 Comment(0)
F
1

Hi Guys I Did Something like this. For N-th Depth Copy Folder and Files to copy. Which Allows you to copy all the directory structure to copy from Android AssetManager :)

    private void manageAssetFolderToSDcard()
    {

        try
        {
            String arg_assetDir = getApplicationContext().getPackageName();
            String arg_destinationDir = FRConstants.ANDROID_DATA + arg_assetDir;
            File FolderInCache = new File(arg_destinationDir);
            if (!FolderInCache.exists())
            {
                copyDirorfileFromAssetManager(arg_assetDir, arg_destinationDir);
            }
        } catch (IOException e1)
        {

            e1.printStackTrace();
        }

    }


    public String copyDirorfileFromAssetManager(String arg_assetDir, String arg_destinationDir) throws IOException
    {
        File sd_path = Environment.getExternalStorageDirectory(); 
        String dest_dir_path = sd_path + addLeadingSlash(arg_destinationDir);
        File dest_dir = new File(dest_dir_path);

        createDir(dest_dir);

        AssetManager asset_manager = getApplicationContext().getAssets();
        String[] files = asset_manager.list(arg_assetDir);

        for (int i = 0; i < files.length; i++)
        {

            String abs_asset_file_path = addTrailingSlash(arg_assetDir) + files[i];
            String sub_files[] = asset_manager.list(abs_asset_file_path);

            if (sub_files.length == 0)
            {
                // It is a file
                String dest_file_path = addTrailingSlash(dest_dir_path) + files[i];
                copyAssetFile(abs_asset_file_path, dest_file_path);
            } else
            {
                // It is a sub directory
                copyDirorfileFromAssetManager(abs_asset_file_path, addTrailingSlash(arg_destinationDir) + files[i]);
            }
        }

        return dest_dir_path;
    }


    public void copyAssetFile(String assetFilePath, String destinationFilePath) throws IOException
    {
        InputStream in = getApplicationContext().getAssets().open(assetFilePath);
        OutputStream out = new FileOutputStream(destinationFilePath);

        byte[] buf = new byte[1024];
        int len;
        while ((len = in.read(buf)) > 0)
            out.write(buf, 0, len);
        in.close();
        out.close();
    }

    public String addTrailingSlash(String path)
    {
        if (path.charAt(path.length() - 1) != '/')
        {
            path += "/";
        }
        return path;
    }

    public String addLeadingSlash(String path)
    {
        if (path.charAt(0) != '/')
        {
            path = "/" + path;
        }
        return path;
    }

    public void createDir(File dir) throws IOException
    {
        if (dir.exists())
        {
            if (!dir.isDirectory())
            {
                throw new IOException("Can't create directory, a file is in the way");
            }
        } else
        {
            dir.mkdirs();
            if (!dir.isDirectory())
            {
                throw new IOException("Unable to create directory");
            }
        }
    }

In the end Create a Asynctask:

    private class ManageAssetFolders extends AsyncTask<Void, Void, Void>
    {

        @Override
        protected Void doInBackground(Void... arg0)
        {
            manageAssetFolderToSDcard();
            return null;
        }

    }

call it From your activity:

    new ManageAssetFolders().execute();
Fortnightly answered 23/9, 2014 at 6:31 Comment(0)
P
1
import android.app.Activity;
import android.content.Intent;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.Environment;
import android.os.Bundle;
import android.util.Log;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;


public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        copyReadAssets();
    }


    private void copyReadAssets()
    {
        AssetManager assetManager = getAssets();

        InputStream in = null;
        OutputStream out = null;

        String strDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+ File.separator + "Pdfs";
        File fileDir = new File(strDir);
        fileDir.mkdirs();   // crear la ruta si no existe
        File file = new File(fileDir, "example2.pdf");



        try
        {

            in = assetManager.open("example.pdf");  //leer el archivo de assets
            out = new BufferedOutputStream(new FileOutputStream(file)); //crear el archivo


            copyFile(in, out);
            in.close();
            in = null;
            out.flush();
            out.close();
            out = null;
        } catch (Exception e)
        {
            Log.e("tag", e.getMessage());
        }

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(Uri.parse("file://" + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + File.separator + "Pdfs" + "/example2.pdf"), "application/pdf");
        startActivity(intent);
    }

    private void copyFile(InputStream in, OutputStream out) throws IOException
    {
        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1)
        {
            out.write(buffer, 0, read);
        }
    }
}

change parts of code like these:

out = new BufferedOutputStream(new FileOutputStream(file));

the before example is for Pdfs, in case of to example .txt

FileOutputStream fos = new FileOutputStream(file);
Pallbearer answered 28/5, 2015 at 21:36 Comment(0)
S
1

Slight modification of above answer to copy a folder recursively and to accommodate custom destination.

public void copyFileOrDir(String path, String destinationDir) {
    AssetManager assetManager = this.getAssets();
    String assets[] = null;
    try {
        assets = assetManager.list(path);
        if (assets.length == 0) {
            copyFile(path,destinationDir);
        } else {
            String fullPath = destinationDir + "/" + path;
            File dir = new File(fullPath);
            if (!dir.exists())
                dir.mkdir();
            for (int i = 0; i < assets.length; ++i) {
                copyFileOrDir(path + "/" + assets[i], destinationDir + path + "/" + assets[i]);
            }
        }
    } catch (IOException ex) {
        Log.e("tag", "I/O Exception", ex);
    }
}

private void copyFile(String filename, String destinationDir) {
    AssetManager assetManager = this.getAssets();
    String newFileName = destinationDir + "/" + filename;

    InputStream in = null;
    OutputStream out = null;
    try {
        in = assetManager.open(filename);
        out = new FileOutputStream(newFileName);

        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
    } catch (Exception e) {
        Log.e("tag", e.getMessage());
    }
    new File(newFileName).setExecutable(true, false);
}
Simas answered 19/7, 2016 at 16:37 Comment(0)
X
1

For those who are updating to Kotlin:

Following this steps to avoid FileUriExposedExceptions, supposing user has granted WRITE_EXTERNAL_STORAGE permission and your file is in assets/pdfs/mypdf.pdf.

private fun openFile() {
    var inputStream: InputStream? = null
    var outputStream: OutputStream? = null
    try {
        val file = File("${activity.getExternalFilesDir(null)}/$PDF_FILE_NAME")
        if (!file.exists()) {
            inputStream = activity.assets.open("$PDF_ASSETS_PATH/$PDF_FILE_NAME")
            outputStream = FileOutputStream(file)
            copyFile(inputStream, outputStream)
        }

        val uri = FileProvider.getUriForFile(
            activity,
            "${BuildConfig.APPLICATION_ID}.provider.GenericFileProvider",
            file
        )
        val intent = Intent(Intent.ACTION_VIEW).apply {
            setDataAndType(uri, "application/pdf")
            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
        }
        activity.startActivity(intent)
    } catch (ex: IOException) {
        ex.printStackTrace()
    } catch (ex: ActivityNotFoundException) {
        ex.printStackTrace()
    } finally {
        inputStream?.close()
        outputStream?.flush()
        outputStream?.close()
    }
}

@Throws(IOException::class)
private fun copyFile(input: InputStream, output: OutputStream) {
    val buffer = ByteArray(1024)
    var read: Int = input.read(buffer)
    while (read != -1) {
        output.write(buffer, 0, read)
        read = input.read(buffer)
    }
}

companion object {
    private const val PDF_ASSETS_PATH = "pdfs"
    private const val PDF_FILE_NAME = "mypdf.pdf"
}
Xylotomy answered 31/12, 2018 at 11:56 Comment(1)
When using FileOutputStream, flush() is useless, because the implemention of flush() in OutputStream is empty. And you can also use Stream.use{} to omit the call of closeHunterhunting
P
1

That is my personalized text extractor class, hope that will be usefull.

package lorenzo.morelli.platedetector;

import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;

import com.googlecode.tesseract.android.TessBaseAPI;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class TextExtractor {

    private final Context context;
    private final String dirName;
    private final String language;

    public TextExtractor(final Context context, final String dirName, final String language) {
        this.context = context;
        this.dirName = dirName;
        this.language = language;
    }

    public String extractText(final Bitmap bitmap) {
        final TessBaseAPI tessBaseApi = new TessBaseAPI();
        final String datapath = this.context.getFilesDir()+ "/tesseract/";
        checkFile(new File(datapath + this.dirName + "/"), datapath, this.dirName, this.language);

        tessBaseApi.init(datapath, this.language);
        tessBaseApi.setImage(bitmap);
        final String extractedText = tessBaseApi.getUTF8Text();
        tessBaseApi.end();
        return extractedText;
    }

    private void checkFile(final File dir, final String datapath, final String dirName, final String language) {
        //directory does not exist, but we can successfully create it
        if (!dir.exists()&& dir.mkdirs()) {
            copyFiles(datapath, dirName, language);
        } //The directory exists, but there is no data file in it
        if(dir.exists()) {
            final String datafilepath = datapath + "/" + dirName + "/" + language + ".traineddata";
            final File datafile = new File(datafilepath);
            if (!datafile.exists()) {
                copyFiles(datapath, dirName, language);
            }
        }
    }

    private void copyFiles(final String datapath, final String dirName, final String language) {
        try {
            //location we want the file to be at
            final String filepath = datapath + "/" + dirName + "/" + language + ".traineddata";

            //get access to AssetManager
            final AssetManager assetManager = this.context.getAssets();

            //open byte streams for reading/writing
            final InputStream instream = assetManager.open(dirName + "/" + language + ".traineddata");
            final OutputStream outstream = new FileOutputStream(filepath);

            //copy the file to the location specified by filepath
            byte[] buffer = new byte[1024];
            int read;
            while ((read = instream.read(buffer)) != -1) {
                outstream.write(buffer, 0, read);
            }
            outstream.flush();
            outstream.close();
            instream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

To use that you need traineddata file. You can download trainddata file from this link.

Once you’ve downloaded the traineddata file you want, you need to make an Android Resource directory named assets in your android project. In the newly created assets folder, you need to create a regular directory named “tessdata” where you can place your traineddata files. Finally you have to init the "TextExtractor" class in your MainActivity.

final TextExtractor textExtractor = new TextExtractor(this, "tessdata", "eng");

First parameter is the context, the second one is the name of directory just created and the last one is the language of traineddata just downloaded.

To extract text you have to call the "extractText" method:

final String text = textExtractor.extractText(imageWithText);

Note that extractText need a BitMap image to work!! You can create a BitMap image from your drawable file with this line:

final BitMap image = BitmapFactory.decodeResource(getResources(), R.drawable.test_image);

If you need more support, I suggest you to follow this usefull guide: https://github.com/SamVanRoy/Android_OCR_App

Pyrophoric answered 19/8, 2020 at 13:38 Comment(0)
H
1

In Kotlin it can be done with one line!

Add extension fun to InputStream

fun InputStream.toFile(to: File){
    this.use { input->
        to.outputStream().use { out->
            input.copyTo(out)
        }
    }
}

then use it

MainActivity.kt

assets.open("test.zip").toFile(File(filesDir,"test.zip"))
Hilar answered 30/5, 2022 at 13:5 Comment(0)
D
0

This is by far the best solution I have been able to find on the internet. I've used the following link https://gist.github.com/mhasby/026f02b33fcc4207b302a60645f6e217,
but it had a single error which I fixed and then it works like a charm. Here's my code. You can easily use it as it is an independent java class.

public class CopyAssets {
public static void copyAssets(Context context) {
    AssetManager assetManager = context.getAssets();
    String[] files = null;
    try {
        files = assetManager.list("");
    } catch (IOException e) {
        Log.e("tag", "Failed to get asset file list.", e);
    }
    if (files != null) for (String filename : files) {
        InputStream in = null;
        OutputStream out = null;
        try {
            in = assetManager.open(filename);

            out = new FileOutputStream(Environment.getExternalStorageDirectory()+"/www/resources/" + filename);
            copyFile(in, out);
        } catch(IOException e) {
            Log.e("tag", "Failed to copy asset file: " + filename, e);
        }
        finally {
            if (in != null) {
                try {
                    in.close();
                    in = null;
                } catch (IOException e) {

                }
            }
            if (out != null) {
                try {
                    out.flush();
                    out.close();
                    out = null;
                } catch (IOException e) {

                }
            }
        }
    }
}

public static void copyFile(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[1024];
    int read;
    while((read = in.read(buffer)) != -1){
        out.write(buffer, 0, read);
    }
}}

As you can see, just create an instance of CopyAssets in your java class which has an activity. Now this part is important, as far as my testing and researching on the internet, You cannot use AssetManager if the class has no activity . It has something to do with the context of the java class.
Now, the c.copyAssets(getApplicationContext()) is an easy way to access the method, where c is and instance of CopyAssets class. As per my requirement, I allowed the program to copy all my resource files inside the asset folder to the /www/resources/ of my internal directory.
You can easily find out the part where you need to make changes to the directory as per your use. Feel free to ping me if you need any help.

Debutante answered 31/5, 2018 at 10:5 Comment(0)
P
0

You can also use Guava's ByteStream to copy the files from the assets folder to the SD card. This is the solution I ended up with which copies files recursively from the assets folder to the SD card:

/**
 * Copies all assets in an assets directory to the SD file system.
 */
public class CopyAssetsToSDHelper {

    public static void copyAssets(String assetDir, String targetDir, Context context) 
        throws IOException {
        AssetManager assets = context.getAssets();
        String[] list = assets.list(assetDir);
        for (String f : Objects.requireNonNull(list)) {
            if (f.indexOf(".") > 1) { // check, if this is a file
                File outFile = new File(context.getExternalFilesDir(null), 
                    String.format("%s/%s", targetDir, f));
                File parentFile = outFile.getParentFile();
                if (!Objects.requireNonNull(parentFile).exists()) {
                    if (!parentFile.mkdirs()) {
                        throw new IOException(String.format("Could not create directory %s.", 
                            parentFile));
                    }
                }
                try (InputStream fin = assets.open(String.format("%s/%s", assetDir, f));
                     OutputStream fout = new FileOutputStream(outFile)) {
                    ByteStreams.copy(fin, fout);
                }
            } else { // This is a directory
                copyAssets(String.format("%s/%s", assetDir, f), String.format("%s/%s", targetDir, f), 
                    context);
            }
        }
    }

}
Pock answered 10/7, 2020 at 9:27 Comment(0)
T
-1

Use AssetManager, it allows to read the files in the assets. Then use regular Java IO to write the files to sdcard.

Google is your friend, search for an example.

Theoretical answered 15/12, 2010 at 7:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.