I'm trying to use a directory that I have in my assets folder and access it as a File. Is it possible to access something in the Assets directory as a File? If not, how can I copy a directory from the Assets folder to the application's local directory?
I would copy a file like so:
try
{
InputStream stream = this.getAssets().open("myFile");
OutputStream output = new BufferedOutputStream(new FileOutputStream(this.getFilesDir() + "/myNewFile"));
byte data[] = new byte[1024];
int count;
while((count = stream.read(data)) != -1)
{
output.write(data, 0, count);
}
output.flush();
output.close();
stream.close();
}
catch(IOException e)
{
e.printStackTrace();
}
However, I'm not sure how I would be able to do this for a directory.
I would rather not build my infrastructure around something that doesn't work, so how would I copy a directory from Assets to a local directory, or is it possible to access a directory in my Assets as a File?
EDIT
This is how I solved it for my own project:
InputStream stream = null;
OutputStream output = null;
for(String fileName : this.getAssets().list("demopass"))
{
stream = this.getAssets().open("directoryName/" + fileName);
output = new BufferedOutputStream(new FileOutputStream(this.getFilesDir() + "/newDirectory/" + fileName));
byte data[] = new byte[1024];
int count;
while((count = stream.read(data)) != -1)
{
output.write(data, 0, count);
}
output.flush();
output.close();
stream.close();
stream = null;
output = null;
}
As suggested by dmaxi in comment above, you can use his link, with this code:
void displayFiles (AssetManager mgr, String path) {
try {
String list[] = mgr.list(path);
if (list != null)
for (int i=0; i<list.length; ++i)
{
Log.v("Assets:", path +"/"+ list[i]);
displayFiles(mgr, path + "/" + list[i]);
}
} catch (IOException e) {
Log.v("List error:", "can't list" + path);
}
}
I took it on this link.
Maybe you can combine this code with precedent one.
EDIT: see also AssetManager.
private void copyFolder(String name) {
// "Name" is the name of your folder!
AssetManager assetManager = getAssets();
String[] files = null;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// We can read and write the media
// Checking file on assets subfolder
try {
files = assetManager.list(name);
} catch (IOException e) {
Log.e("ERROR", "Failed to get asset file list.", e);
}
// Analyzing all file on assets subfolder
for(String filename : files) {
InputStream in = null;
OutputStream out = null;
// First: checking if there is already a target folder
File folder = new File(Environment.getExternalStorageDirectory() + "/yourTargetFolder/" + name);
boolean success = true;
if (!folder.exists()) {
success = folder.mkdir();
}
if (success) {
// Moving all the files on external SD
try {
in = assetManager.open(name + "/" +filename);
out = new FileOutputStream(Environment.getExternalStorageDirectory() + "/yourTargetFolder/" + name + "/" + filename);
Log.i("WEBVIEW", Environment.getExternalStorageDirectory() + "/yourTargetFolder/" + name + "/" + filename);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch(IOException e) {
Log.e("ERROR", "Failed to copy asset file: " + filename, e);
} finally {
// Edit 3 (after MMs comment)
in.close();
in = null;
out.flush();
out.close();
out = null;
}
}
else {
// Do something else on failure
}
}
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// We can only read the media
} else {
// Something else is wrong. It may be one of many other states, but all we need
// is to know is we can neither read nor write
}
}
// Method used by copyAssets() on purpose to copy a file.
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);
}
}
EDIT 2: i'have added an example above: this piece of code copy only a specific folder from assets, to sd card. Let me know if it works!
Here is a recursive function to do this - copyAssetFolder.
public static boolean copyAssetFolder(Context context, String srcName, String dstName) {
try {
boolean result = true;
String fileList[] = context.getAssets().list(srcName);
if (fileList == null) return false;
if (fileList.length == 0) {
result = copyAssetFile(context, srcName, dstName);
} else {
File file = new File(dstName);
result = file.mkdirs();
for (String filename : fileList) {
result &= copyAssetFolder(context, srcName + File.separator + filename, dstName + File.separator + filename);
}
}
return result;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
public static boolean copyAssetFile(Context context, String srcName, String dstName) {
try {
InputStream in = context.getAssets().open(srcName);
File outFile = new File(dstName);
OutputStream out = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
out.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
Or the same in Kotlin
fun AssetManager.copyAssetFolder(srcName: String, dstName: String): Boolean {
return try {
var result = true
val fileList = this.list(srcName) ?: return false
if (fileList.isEmpty()) {
result = copyAssetFile(srcName, dstName)
} else {
val file = File(dstName)
result = file.mkdirs()
for (filename in fileList) {
result = result and copyAssetFolder(
srcName + separator.toString() + filename,
dstName + separator.toString() + filename
)
}
}
result
} catch (e: IOException) {
e.printStackTrace()
false
}
}
fun AssetManager.copyAssetFile(srcName: String, dstName: String): Boolean {
return try {
val inStream = this.open(srcName)
val outFile = File(dstName)
val out: OutputStream = FileOutputStream(outFile)
val buffer = ByteArray(1024)
var read: Int
while (inStream.read(buffer).also { read = it } != -1) {
out.write(buffer, 0, read)
}
inStream.close()
out.close()
true
} catch (e: IOException) {
e.printStackTrace()
false
}
}
You can use following method for copying your asset folder to a location in your SD Card. From your calling method just call moveAssetToStorageDir("") for moving entire asset folder. In case of sub folders you can specify the relative path inside the asset folder.
public void moveAssetToStorageDir(String path){
File file = getExternalFilesDir(null);
String rootPath = file.getPath() + "/" + path;
try{
String [] paths = getAssets().list(path);
for(int i=0; i<paths.length; i++){
if(paths[i].indexOf(".")==-1){
File dir = new File(rootPath + paths[i]);
dir.mkdir();
moveAssetToStorageDir(paths[i]);
}else {
File dest = null;
InputStream in = null;
if(path.length() == 0) {
dest = new File(rootPath + paths[i]);
in = getAssets().open(paths[i]);
}else{
dest = new File(rootPath + "/" + paths[i]);
in = getAssets().open(path + "/" + paths[i]);
}
dest.createNewFile();
FileOutputStream out = new FileOutputStream(dest);
byte [] buff = new byte[in.available()];
in.read(buff);
out.write(buff);
out.close();
in.close();
}
}
}catch (Exception exp){
exp.printStackTrace();
}
}
Here is the clean version of the OP's answer.
public void copyAssetFolderToFolder(Context activity, String assetsFolder, File destinationFolder) {
InputStream stream = null;
OutputStream output = null;
try {
for (String fileName : activity.getAssets().list(assetsFolder)) {
stream = activity.getAssets().open(assetsFolder + ((assetsFolder.endsWith(File.pathSeparator))?"":File.pathSeparator) + fileName);
output = new BufferedOutputStream(new FileOutputStream(new File(destinationFolder, fileName)));
byte data[] = new byte[1024];
int count;
while ((count = stream.read(data)) != -1) {
output.write(data, 0, count);
}
output.flush();
output.close();
stream.close();
stream = null;
output = null;
}
} catch (/*any*/Exception e){e.printStackTrace();}
}
For future reference, please save everyone the trouble and post contextually complete source listings. This site can be a great coding resource for beginners and experts, if only you would post complete answers. One cannot assume that anyone else "understands" where a random block of code belongs, or the context that the code is supposed to be executed within.
This sample calls for the context of an activity, which houses the getAssets() method. Within the android platform, their are other classes besides Activity which can supply this context. One example is the (generic reference) Service class.
Moving an arbitrary folder of directories and files from Assets
The thing is... Assets are special. You cannot wrap it in a File object and ask isDirectory() and you cannot pass these assets into the NDK. So it is better to wrap them up and move them to a cache directory or onto the SDCard which is why you're here.
I've seen many SO answers that involve some version of rolling through an array of fileOrDirectoryName strings and then creating directories followed by a recursive call and copying individual files. Which leads you to create a folder or file and you cannot tell from an asset which you have.
Make it a Zip file
My recommendation is to take each arbitrary collection of assets that you want to ship to the SDCard or an internal cache folder and Zip it up. The problem is structured in an way more compatible with the Assets concept.
AssetManager assetManager = context.getAssets();
String fullAssetPath = fromAssetPath + "/" + zipFilename;
String toPath = "/wherever/I/want";
try {
InputStream inputStream = assetManager.open(fullAssetPath);
ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(inputStream));
ZipEntry zipEntry;
byte[] buffer = new byte[8192];
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String fileOrDirectory = zipEntry.getName();
Uri.Builder builder = new Uri.Builder();
builder.scheme("file");
builder.appendPath(toPath);
builder.appendPath(fileOrDirectory);
String fullToPath = builder.build().getPath();
if (zipEntry.isDirectory()) {
File directory = new File(fullToPath);
directory.mkdirs();
continue;
}
FileOutputStream fileOutputStream = new FileOutputStream(fullToPath);
while ((count = zipInputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, count);
}
fileOutputStream.close();
zipInputStream.closeEntry();
}
zipInputStream.close();
} catch (IOException e) {
Log.e(TAG, e.getLocalizedMessage());
}
Small note about buffer sizes
I've seen a lot of examples involving very small buffer sizes, for example 1024. Unless you just want to waste time feel free to try larger byte buffer sizes. Even my choice of 8192 is probably small on modern hardware.
Avoiding Stringy paths
Notice the use of Uri.Builder to construct the path. I much prefer this style of path construction over directory + "/" + file. Then you're in the business, for the sake of consistency avoiding assigning String d = "myDirectory/" or String f = "/file.txt" and other such string hacking nonsense.
Here's a recursive solution written in kotlin. It works with both files and dirs.
Usage - copyAssetDir(context, "<asset path>", "<dest dir>")
import android.content.Context
import java.io.File
import java.io.FileOutputStream
fun copyAssetDir(context: Context, assetPath: String, destDirPath: String) {
walkAssetDir(context, assetPath) {
copyAssetFile(context, it, "$destDirPath/$it")
}
}
fun walkAssetDir(context: Context, assetPath: String, callback: ((String) -> Unit)) {
val children = context.assets.list(assetPath) ?: return
if (children.isEmpty()) {
callback(assetPath)
} else {
for (child in children) {
walkAssetDir(context, "$assetPath/$child", callback)
}
}
}
fun copyAssetFile(context: Context, assetPath: String, destPath: String): File {
val destFile = File(destPath)
File(destFile.parent).mkdirs()
destFile.createNewFile()
context.assets.open(assetPath).use { src ->
FileOutputStream(destFile).use { dest ->
src.copyTo(dest)
}
}
return destFile
}
This is code for copy assets folder with directory and files both copy into sdcard folder...
this one works perfectly for me...
public 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());
}
}
I put a zip file in the android assets. How do i extract the file in the android internal storage? I know how to get the file, but i don't know how to extract it. This is my code..
Util zip ;
zip = new Util();
zip.copyFileFromAsset(this, "myfile.zip", getExternalStorage()+
"/android/data/edu.binus.profile/");
Thanks for helping :D
This piece of code will help you....Just pass the zipfile location and the location where you want the extracted files to be saved to this class while making an object...and call unzip method...
public class Decompress {
private String zip;
private String loc;
public Decompress(String zipFile, String location) {
zip = zipFile;
loc = location;
dirChecker("");
}
public void unzip() {
try {
FileInputStream fin = new FileInputStream(zip);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
Log.v("Decompress", "Unzipping " + ze.getName());
if(ze.isDirectory()) {
dirChecker(ze.getName());
} else {
FileOutputStream fout = new FileOutputStream(loc + ze.getName());
for (int c = zin.read(); c != -1; c = zin.read()) {
fout.write(c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
} catch(Exception e) {
Log.e("Decompress", "unzip", e);
}
}
private void dirChecker(String dir) {
File f = new File(_location + dir);
if(!f.isDirectory()) {
f.mkdirs();
}
}
}
Based on Sreedev R solution,
I added the option to read the file from assets and use buffer:
package com.pixoneye.api.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import android.content.Context;
import android.util.Log;
public class Decompress {
private static final int BUFFER_SIZE = 1024 * 10;
private static final String TAG = "Decompress";
public static void unzipFromAssets(Context context, String zipFile, String destination) {
try {
if (destination == null || destination.length() == 0)
destination = context.getFilesDir().getAbsolutePath();
InputStream stream = context.getAssets().open(zipFile);
unzip(stream, destination);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void unzip(String zipFile, String location) {
try {
FileInputStream fin = new FileInputStream(zipFile);
unzip(fin, location);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void unzip(InputStream stream, String destination) {
dirChecker(destination, "");
byte[] buffer = new byte[BUFFER_SIZE];
try {
ZipInputStream zin = new ZipInputStream(stream);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
Log.v(TAG, "Unzipping " + ze.getName());
if (ze.isDirectory()) {
dirChecker(destination, ze.getName());
} else {
File f = new File(destination, ze.getName());
if (!f.exists()) {
boolean success = f.createNewFile();
if (!success) {
Log.w(TAG, "Failed to create file " + f.getName());
continue;
}
FileOutputStream fout = new FileOutputStream(f);
int count;
while ((count = zin.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
zin.closeEntry();
fout.close();
}
}
}
zin.close();
} catch (Exception e) {
Log.e(TAG, "unzip", e);
}
}
private static void dirChecker(String destination, String dir) {
File f = new File(destination, dir);
if (!f.isDirectory()) {
boolean success = f.mkdirs();
if (!success) {
Log.w(TAG, "Failed to create folder " + f.getName());
}
}
}
}
Maybe you should try using a FileOutputStream in combination with an inputstream from the zip file. With a package file, this should work.
To quote #wordy from this question:
PackageManager pm = context.getPackageManager();
String apkFile = pm.getApplicationInfo(context.getPackageName(), 0).sourceDir;
ZipFile zipFile = new ZipFile(apkFile);
ZipEntry entry = zipFile.getEntry("assets/FILENAME");
myInput = zipFile.getInputStream(entry);
myOutput = new FileOutputStream(file);
byte[] buffer = new byte[1024*4];
int length;
int total = 0;
int counter = 1;
while ((length = myInput.read(buffer)) > 0) {
total += length;
counter++;
if (counter % 32 == 0) {
publishProgress(total);
}
myOutput.write(buffer, 0, length);
}
Looks like there may be problems with ProGuard but hopefully the code sample works for you.
I haven't tested yet,but while doing a project on OCR I came across this library,where there is method of unzipping a downloaded file from the net. The exact method for unzipping file is installZipFromAssets(String sourceFilename,File destinationDir,File destinationFile) found under this class.Hope this is what you are looking for
You can also make use of the zip4j external library that provides additional features like encryption. Also, it has functions to extract files to a particular location provided the path.
I'm trying to use a directory that I have in my assets folder and access it as a File. Is it possible to access something in the Assets directory as a File? If not, how can I copy a directory from the Assets folder to the application's local directory?
I would copy a file like so:
try
{
InputStream stream = this.getAssets().open("myFile");
OutputStream output = new BufferedOutputStream(new FileOutputStream(this.getFilesDir() + "/myNewFile"));
byte data[] = new byte[1024];
int count;
while((count = stream.read(data)) != -1)
{
output.write(data, 0, count);
}
output.flush();
output.close();
stream.close();
}
catch(IOException e)
{
e.printStackTrace();
}
However, I'm not sure how I would be able to do this for a directory.
I would rather not build my infrastructure around something that doesn't work, so how would I copy a directory from Assets to a local directory, or is it possible to access a directory in my Assets as a File?
EDIT
This is how I solved it for my own project:
InputStream stream = null;
OutputStream output = null;
for(String fileName : this.getAssets().list("demopass"))
{
stream = this.getAssets().open("directoryName/" + fileName);
output = new BufferedOutputStream(new FileOutputStream(this.getFilesDir() + "/newDirectory/" + fileName));
byte data[] = new byte[1024];
int count;
while((count = stream.read(data)) != -1)
{
output.write(data, 0, count);
}
output.flush();
output.close();
stream.close();
stream = null;
output = null;
}
As suggested by dmaxi in comment above, you can use his link, with this code:
void displayFiles (AssetManager mgr, String path) {
try {
String list[] = mgr.list(path);
if (list != null)
for (int i=0; i<list.length; ++i)
{
Log.v("Assets:", path +"/"+ list[i]);
displayFiles(mgr, path + "/" + list[i]);
}
} catch (IOException e) {
Log.v("List error:", "can't list" + path);
}
}
I took it on this link.
Maybe you can combine this code with precedent one.
EDIT: see also AssetManager.
private void copyFolder(String name) {
// "Name" is the name of your folder!
AssetManager assetManager = getAssets();
String[] files = null;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// We can read and write the media
// Checking file on assets subfolder
try {
files = assetManager.list(name);
} catch (IOException e) {
Log.e("ERROR", "Failed to get asset file list.", e);
}
// Analyzing all file on assets subfolder
for(String filename : files) {
InputStream in = null;
OutputStream out = null;
// First: checking if there is already a target folder
File folder = new File(Environment.getExternalStorageDirectory() + "/yourTargetFolder/" + name);
boolean success = true;
if (!folder.exists()) {
success = folder.mkdir();
}
if (success) {
// Moving all the files on external SD
try {
in = assetManager.open(name + "/" +filename);
out = new FileOutputStream(Environment.getExternalStorageDirectory() + "/yourTargetFolder/" + name + "/" + filename);
Log.i("WEBVIEW", Environment.getExternalStorageDirectory() + "/yourTargetFolder/" + name + "/" + filename);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch(IOException e) {
Log.e("ERROR", "Failed to copy asset file: " + filename, e);
} finally {
// Edit 3 (after MMs comment)
in.close();
in = null;
out.flush();
out.close();
out = null;
}
}
else {
// Do something else on failure
}
}
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// We can only read the media
} else {
// Something else is wrong. It may be one of many other states, but all we need
// is to know is we can neither read nor write
}
}
// Method used by copyAssets() on purpose to copy a file.
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);
}
}
EDIT 2: i'have added an example above: this piece of code copy only a specific folder from assets, to sd card. Let me know if it works!
Here is a recursive function to do this - copyAssetFolder.
public static boolean copyAssetFolder(Context context, String srcName, String dstName) {
try {
boolean result = true;
String fileList[] = context.getAssets().list(srcName);
if (fileList == null) return false;
if (fileList.length == 0) {
result = copyAssetFile(context, srcName, dstName);
} else {
File file = new File(dstName);
result = file.mkdirs();
for (String filename : fileList) {
result &= copyAssetFolder(context, srcName + File.separator + filename, dstName + File.separator + filename);
}
}
return result;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
public static boolean copyAssetFile(Context context, String srcName, String dstName) {
try {
InputStream in = context.getAssets().open(srcName);
File outFile = new File(dstName);
OutputStream out = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
out.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
Or the same in Kotlin
fun AssetManager.copyAssetFolder(srcName: String, dstName: String): Boolean {
return try {
var result = true
val fileList = this.list(srcName) ?: return false
if (fileList.isEmpty()) {
result = copyAssetFile(srcName, dstName)
} else {
val file = File(dstName)
result = file.mkdirs()
for (filename in fileList) {
result = result and copyAssetFolder(
srcName + separator.toString() + filename,
dstName + separator.toString() + filename
)
}
}
result
} catch (e: IOException) {
e.printStackTrace()
false
}
}
fun AssetManager.copyAssetFile(srcName: String, dstName: String): Boolean {
return try {
val inStream = this.open(srcName)
val outFile = File(dstName)
val out: OutputStream = FileOutputStream(outFile)
val buffer = ByteArray(1024)
var read: Int
while (inStream.read(buffer).also { read = it } != -1) {
out.write(buffer, 0, read)
}
inStream.close()
out.close()
true
} catch (e: IOException) {
e.printStackTrace()
false
}
}
You can use following method for copying your asset folder to a location in your SD Card. From your calling method just call moveAssetToStorageDir("") for moving entire asset folder. In case of sub folders you can specify the relative path inside the asset folder.
public void moveAssetToStorageDir(String path){
File file = getExternalFilesDir(null);
String rootPath = file.getPath() + "/" + path;
try{
String [] paths = getAssets().list(path);
for(int i=0; i<paths.length; i++){
if(paths[i].indexOf(".")==-1){
File dir = new File(rootPath + paths[i]);
dir.mkdir();
moveAssetToStorageDir(paths[i]);
}else {
File dest = null;
InputStream in = null;
if(path.length() == 0) {
dest = new File(rootPath + paths[i]);
in = getAssets().open(paths[i]);
}else{
dest = new File(rootPath + "/" + paths[i]);
in = getAssets().open(path + "/" + paths[i]);
}
dest.createNewFile();
FileOutputStream out = new FileOutputStream(dest);
byte [] buff = new byte[in.available()];
in.read(buff);
out.write(buff);
out.close();
in.close();
}
}
}catch (Exception exp){
exp.printStackTrace();
}
}
Here is the clean version of the OP's answer.
public void copyAssetFolderToFolder(Context activity, String assetsFolder, File destinationFolder) {
InputStream stream = null;
OutputStream output = null;
try {
for (String fileName : activity.getAssets().list(assetsFolder)) {
stream = activity.getAssets().open(assetsFolder + ((assetsFolder.endsWith(File.pathSeparator))?"":File.pathSeparator) + fileName);
output = new BufferedOutputStream(new FileOutputStream(new File(destinationFolder, fileName)));
byte data[] = new byte[1024];
int count;
while ((count = stream.read(data)) != -1) {
output.write(data, 0, count);
}
output.flush();
output.close();
stream.close();
stream = null;
output = null;
}
} catch (/*any*/Exception e){e.printStackTrace();}
}
For future reference, please save everyone the trouble and post contextually complete source listings. This site can be a great coding resource for beginners and experts, if only you would post complete answers. One cannot assume that anyone else "understands" where a random block of code belongs, or the context that the code is supposed to be executed within.
This sample calls for the context of an activity, which houses the getAssets() method. Within the android platform, their are other classes besides Activity which can supply this context. One example is the (generic reference) Service class.
Moving an arbitrary folder of directories and files from Assets
The thing is... Assets are special. You cannot wrap it in a File object and ask isDirectory() and you cannot pass these assets into the NDK. So it is better to wrap them up and move them to a cache directory or onto the SDCard which is why you're here.
I've seen many SO answers that involve some version of rolling through an array of fileOrDirectoryName strings and then creating directories followed by a recursive call and copying individual files. Which leads you to create a folder or file and you cannot tell from an asset which you have.
Make it a Zip file
My recommendation is to take each arbitrary collection of assets that you want to ship to the SDCard or an internal cache folder and Zip it up. The problem is structured in an way more compatible with the Assets concept.
AssetManager assetManager = context.getAssets();
String fullAssetPath = fromAssetPath + "/" + zipFilename;
String toPath = "/wherever/I/want";
try {
InputStream inputStream = assetManager.open(fullAssetPath);
ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(inputStream));
ZipEntry zipEntry;
byte[] buffer = new byte[8192];
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String fileOrDirectory = zipEntry.getName();
Uri.Builder builder = new Uri.Builder();
builder.scheme("file");
builder.appendPath(toPath);
builder.appendPath(fileOrDirectory);
String fullToPath = builder.build().getPath();
if (zipEntry.isDirectory()) {
File directory = new File(fullToPath);
directory.mkdirs();
continue;
}
FileOutputStream fileOutputStream = new FileOutputStream(fullToPath);
while ((count = zipInputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, count);
}
fileOutputStream.close();
zipInputStream.closeEntry();
}
zipInputStream.close();
} catch (IOException e) {
Log.e(TAG, e.getLocalizedMessage());
}
Small note about buffer sizes
I've seen a lot of examples involving very small buffer sizes, for example 1024. Unless you just want to waste time feel free to try larger byte buffer sizes. Even my choice of 8192 is probably small on modern hardware.
Avoiding Stringy paths
Notice the use of Uri.Builder to construct the path. I much prefer this style of path construction over directory + "/" + file. Then you're in the business, for the sake of consistency avoiding assigning String d = "myDirectory/" or String f = "/file.txt" and other such string hacking nonsense.
Here's a recursive solution written in kotlin. It works with both files and dirs.
Usage - copyAssetDir(context, "<asset path>", "<dest dir>")
import android.content.Context
import java.io.File
import java.io.FileOutputStream
fun copyAssetDir(context: Context, assetPath: String, destDirPath: String) {
walkAssetDir(context, assetPath) {
copyAssetFile(context, it, "$destDirPath/$it")
}
}
fun walkAssetDir(context: Context, assetPath: String, callback: ((String) -> Unit)) {
val children = context.assets.list(assetPath) ?: return
if (children.isEmpty()) {
callback(assetPath)
} else {
for (child in children) {
walkAssetDir(context, "$assetPath/$child", callback)
}
}
}
fun copyAssetFile(context: Context, assetPath: String, destPath: String): File {
val destFile = File(destPath)
File(destFile.parent).mkdirs()
destFile.createNewFile()
context.assets.open(assetPath).use { src ->
FileOutputStream(destFile).use { dest ->
src.copyTo(dest)
}
}
return destFile
}
This is code for copy assets folder with directory and files both copy into sdcard folder...
this one works perfectly for me...
public 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());
}
}
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?
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
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());
}
}
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.
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();
}
}
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))
}
}
}
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();
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();
}
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
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.
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);
}
}
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);
}
}
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());
}
}
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
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.
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);
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();
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);
}
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"
}
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
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"))
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.
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);
}
}
}
}
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.