I'm currently developing an android application in which I've to download a .zip archive and then unzip it. The download of the archive is done correctly. I'm able to unzip it manually directly on the phone or on my computer.
However, when I try to unzip programmatically the archive, I get a UTFDataFormatException. I tried to force the system encoding by adding :
System.setProperty("file.encoding", "UTF-8");
Or to process the names of the files :
filename = new String(ze.getName().getBytes("UTF-8"));
Did I miss something in my unzip function ?
private boolean unzip(String path, String zipname)
{
InputStream is;
ZipInputStream zis;
try
{
String filename;
is = new FileInputStream(path + zipname);
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
byte[] buffer = new byte[4096];
int count;
while ((ze = zis.getNextEntry()) != null)
{
filename = ze.getName();
if (ze.isDirectory())
{
File fmd = new File(path + filename);
fmd.mkdirs();
continue;
}
FileOutputStream fout = new FileOutputStream(path + filename);
while ((count = zis.read(buffer)) != -1)
{
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
}
catch(IOException e)
{
e.printStackTrace();
return false;
}
return true;
}
Below the message of the exception :
java.io.UTFDataFormatException: bad byte at 72
After all, I used Zip4j library and it works very well. It's an old library which is not maintained and not callable thanks to gradle, but it fix my problem (http://www.lingala.net/zip4j/).
try
{
ZipFile zipFile = new ZipFile(sourceFile);
zipFile.extractAll(destinationPath);
}
catch (ZipException e)
{
e.printStackTrace();
}
Try this:
public static boolean unzip(String zipFile, String location) {
if (!location.endsWith("/")) {
location += "/";
}
if (!zipFile.endsWith(".zip") || !new File(location + zipFile).exists()) {
return false;
}
int size;
byte[] buffer = new byte[1024];
try {
File f = new File(location);
if (!f.isDirectory()) {
f.mkdirs();
}
ZipInputStream zin = new ZipInputStream(new BufferedInputStream(new FileInputStream(location + zipFile), 1024));
try {
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
String path = location + ze.getName();
File unzipFile = new File(path);
if (ze.isDirectory()) {
if (!unzipFile.isDirectory()) {
unzipFile.mkdirs();
}
} else {
// check for and create parent directories if they don't exist
File parentDir = unzipFile.getParentFile();
if (null != parentDir) {
if (!parentDir.isDirectory()) {
parentDir.mkdirs();
}
}
// unzip the file
FileOutputStream out = new FileOutputStream(unzipFile, false);
BufferedOutputStream fout = new BufferedOutputStream(out, 1024);
try {
while ((size = zin.read(buffer, 0, 1024)) != -1) {
fout.write(buffer, 0, size);
}
zin.closeEntry();
} finally {
try {
fout.flush();
fout.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
} finally {
try {
zin.close();
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
Try this,
public class DecompressZip{
private static final int BUFFER_SIZE=8192;
private String _zipFile;
private String _location;
private byte[] _buffer;
/**
* Constructor.
*
* #param zipFile Fully-qualified path to .zip file
* #param location Fully-qualified path to folder where files should be written.
* Path must have a trailing slash.
*/
public DecompressZip(String zipFile, String location)
{
_zipFile = zipFile;
_location = location;
_buffer = new byte[BUFFER_SIZE];
dirChecker("");
}
public void unzip()
{
FileInputStream fin = null;
ZipInputStream zin = null;
OutputStream fout = null;
File outputDir = new File(_location);
File tmp = null;
try {
fin = new FileInputStream(_zipFile);
zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null)
{
Log.d("Decompress", "Unzipping " + ze.getName());
Log.d("Decompress", "FileSize " + ze.getSize());
Log.d("Decompress", "compressedSize " + ze.getCompressedSize());
if (ze.isDirectory())
{
dirChecker(ze.getName());
}
else
{
tmp = File.createTempFile( "decomp", ".tmp", outputDir );
fout = new BufferedOutputStream(new FileOutputStream(tmp));
DownloadFile.copyStream( zin, fout, _buffer, BUFFER_SIZE );
zin.closeEntry();
fout.close();
fout = null;
tmp.renameTo( new File(_location + ze.getName()) );
tmp = null;
}
}
zin.close();
zin = null;
}
catch (IOException e)
{
throw new RuntimeException(e);
}
finally
{
if ( tmp != null ) { try { tmp.delete(); } catch (Exception ignore) {;} }
if ( fout != null ) { try { fout.close(); } catch (Exception ignore) {;} }
if ( zin != null ) { try { zin.closeEntry(); } catch (Exception ignore) {;} }
if ( fin != null ) { try { fin.close(); } catch (Exception ignore) {;} }
}
}
private void dirChecker(String dir)
{
File f = new File(_location + dir);
if (!f.isDirectory())
{
f.mkdirs();
}
}}
try this below code. I have done this to download zipped images.
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.ParcelFileDescriptor;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class MainActivity extends AppCompatActivity {
private ProgressDialog simpleWaitDialog;
private Bitmap table1,seat1,seat2,seat3,seat4,seat5,dummy;
private ImageView tableIv,seat1Iv,seat2Iv,seat3Iv,seat4Iv,seat5Iv,overlay;
File _zipFile;
InputStream _zipFileStream;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tableIv=(ImageView)findViewById(R.id.iv);
seat1Iv=(ImageView)findViewById(R.id.seat1IV);
seat2Iv=(ImageView)findViewById(R.id.seat2IV);
seat3Iv=(ImageView)findViewById(R.id.seat3IV);
seat4Iv=(ImageView)findViewById(R.id.seat4IV);
seat5Iv=(ImageView)findViewById(R.id.seat5IV);
overlay=(ImageView)findViewById(R.id.overlayIV);
///data/user/0/com.example.ayyappaboddupalli.zipperunzipper/app_zipper1/themeparts1.zip/dummy/6_player.jpg -location where files stored
ContextWrapper wrapper=new ContextWrapper(this);
File sd = wrapper.getDir("zipper1", MODE_PRIVATE);
File dest = new File(sd, "theme1.zip");
File target = new File(sd, "themeparts1.zip");
if(target.exists())
{
commonCaller(dest,target,sd);
}
else
{
new ImageDownloader().execute();
commonCaller(dest,target,sd);
}
}
private void uriToBitmap(Uri selectedFileUri, String name) {
try {
ParcelFileDescriptor parcelFileDescriptor =
getContentResolver().openFileDescriptor(selectedFileUri, "r");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
assignBitmapToView(image,name);
parcelFileDescriptor.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void assignBitmapToView(Bitmap image, String name) {
String toCompare=name.toString().substring(name.lastIndexOf("/")+1,name.lastIndexOf("."));
switch (toCompare)
{
case "dummy":
dummy=image;
overlay.setImageBitmap(dummy);
break;
case "6_player":
table1=image;
tableIv.setImageBitmap(table1);
break;
case "f1":
seat1=image;
seat1Iv.setImageBitmap(seat1);
break;
case "f2":
seat2=image;
seat2Iv.setImageBitmap(seat2);
break;
case "f3":
seat3=image;
seat3Iv.setImageBitmap(seat3);
break;
case "f4":
seat4=image;
seat4Iv.setImageBitmap(seat4);
break;
case "f5":
seat5=image;
seat5Iv.setImageBitmap(seat5);
break;
}
}
public void commonCaller(File dest,File target,File sd)
{
if(sd.exists()) {
// unzip(dest.getAbsolutePath(), target.getAbsolutePath());
try {
unzipFileIntoDirectory(dest,target);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*public void unzip(String _zipFile, String _targetLocation) {
//create target location folder if not exist
_dirChecker(_zipFile);
try {
FileInputStream fin = new FileInputStream(_zipFile);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
//create dir if required while unzipping
if (ze.isDirectory()) {
_dirChecker(ze.getName());
} else {
FileOutputStream fout = new FileOutputStream(_targetLocation + ze.getName());
for (int c = zin.read(); c != -1; c = zin.read()) {
fout.write(c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
} catch (Exception e) {
System.out.println(e);
}
}*/
public void unzip() {
try {
ContextWrapper cws=new ContextWrapper(this);
File sd=cws.getDir("zipper1",MODE_PRIVATE);
File dest=new File(sd,"parts1");
dest.mkdirs();
// final String ROOT_LOCATION = "/sdcard";
Log.i("", "Starting to unzip");
InputStream fin = _zipFileStream;
if(fin == null) {
fin = new FileInputStream(_zipFile);
}
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
Log.v("", "Unzipping " + ze.getName());
if(ze.isDirectory()) {
_dirChecker(dest + "/" + ze.getName());
} else {
FileOutputStream fout = new FileOutputStream(new File(sd.getPath(), ze.getName()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int count;
// reading and writing
while((count = zin.read(buffer)) != -1)
{
baos.write(buffer, 0, count);
byte[] bytes = baos.toByteArray();
fout.write(bytes);
baos.reset();
}
fout.close();
zin.closeEntry();
}
}
zin.close();
Log.i("", "Finished unzip");
} catch(Exception e) {
Log.e("", "Unzip Error", e);
}
}
public void unzipFileIntoDirectory(File archive, File destinationDir)
throws Exception {
final int BUFFER_SIZE = 1024;
BufferedOutputStream dest = null;
FileInputStream fis = new FileInputStream(archive);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
ZipEntry entry;
File destFile;
while ((entry = zis.getNextEntry()) != null) {
destFile=new File(destinationDir,entry.getName());
uriToBitmap(Uri.fromFile(destFile),entry.getName());
// destFile = FilesystemUtils.combineFileNames(destinationDir, entry.getName());
if (entry.isDirectory()) {
destFile.mkdirs();
continue;
} else {
int count;
byte data[] = new byte[BUFFER_SIZE];
destFile.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(destFile);
dest = new BufferedOutputStream(fos, BUFFER_SIZE);
while ((count = zis.read(data, 0, BUFFER_SIZE)) != -1) {
dest.write(data, 0, count);
}
dest.flush();
dest.close();
fos.close();
}
}
zis.close();
fis.close();
}
private void _dirChecker(String dir) {
File f = new File(dir);
if(!f.isDirectory()) {
f.mkdirs();
}
}
private class ImageDownloader extends AsyncTask {
#Override
protected Object doInBackground(Object[] params) {
String url="testing/dummy.zip";
return downloadBitmap(url);
}
#Override
protected void onPreExecute() {
Log.i("Async-Example", "onPreExecute Called");
simpleWaitDialog = new ProgressDialog(MainActivity.this);
simpleWaitDialog.setMessage( "Downloading Image");
simpleWaitDialog.show();
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
simpleWaitDialog.dismiss();
}
private Bitmap downloadBitmap(String url) {
downloadImage(url);
return null;
}
}
public void downloadImage(String urlPart) {
URL url = null;
FileDescriptor fd;
try {
int count;
url = new URL(urlPart);
InputStream input = new BufferedInputStream(url.openStream());
ContextWrapper contextWrapper=new ContextWrapper(this);
File sd = contextWrapper.getDir("zipper1", MODE_PRIVATE);
File dest = new File(sd, "theme1.zip");
_zipFileStream=input;
// File file = new File(downloadLocation);
FileOutputStream output = new FileOutputStream(dest); //context.openFileOutput("content.zip", Context.MODE_PRIVATE);
fd = output.getFD();
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),"success",Toast.LENGTH_LONG).show();
}
});
//old code
/* ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int n = 0;
while (-1 != (n = in.read(buf))) {
out.write(buf, 0, n);
}
out.close();
in.close();
byte[] response = out.toByteArray();
String zipString = Base64.encodeToString(response, Base64.DEFAULT);
ContextWrapper contextWrapper=new ContextWrapper(this);
File sd = contextWrapper.getDir("zipper1", MODE_PRIVATE);
File dest = new File(sd, "theme1.zip");
FileOutputStream fos = new FileOutputStream(dest);
fos.write(zipString.getBytes());
fos.close();
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),"success",Toast.LENGTH_LONG).show();
}
});*/
}
catch (Exception e) {
e.printStackTrace();
}
}
}
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.
How to save a media file (say .mp3) from an existing URI, which I am getting from an Implicit Intent?
Use this method, it works
void savefile(URI sourceuri)
{
String sourceFilename= sourceuri.getPath();
String destinationFilename = android.os.Environment.getExternalStorageDirectory().getPath()+File.separatorChar+"abc.mp3";
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(sourceFilename));
bos = new BufferedOutputStream(new FileOutputStream(destinationFilename, false));
byte[] buf = new byte[1024];
bis.read(buf);
do {
bos.write(buf);
} while(bis.read(buf) != -1);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bis != null) bis.close();
if (bos != null) bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
If Uri is received from Google Drive, it can be a Virtual File Uri too. Check this article from CommonsWare for more information. So you have to consider that condition too while saving file from Uri.
To find if file Uri is virtual or not you can use
private static boolean isVirtualFile(Context context, Uri uri) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (!DocumentsContract.isDocumentUri(context, uri)) {
return false;
}
Cursor cursor = context.getContentResolver().query(
uri,
new String[]{DocumentsContract.Document.COLUMN_FLAGS},
null, null, null);
int flags = 0;
if (cursor.moveToFirst()) {
flags = cursor.getInt(0);
}
cursor.close();
return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
} else {
return false;
}
}
You can get the stream data from this virtual file like this:
private static InputStream getInputStreamForVirtualFile(Context context, Uri uri, String mimeTypeFilter)
throws IOException {
ContentResolver resolver = context.getContentResolver();
String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter);
if (openableMimeTypes == null || openableMimeTypes.length < 1) {
throw new FileNotFoundException();
}
return resolver
.openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
.createInputStream();
}
For finding MIME type try
private static String getMimeType(String url) {
String type = null;
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
if (extension != null) {
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
return type;
}
Overall, you can use
public static boolean saveFile(Context context, String name, Uri sourceuri, String destinationDir, String destFileName) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
InputStream input = null;
boolean hasError = false;
try {
if (isVirtualFile(context, sourceuri)) {
input = getInputStreamForVirtualFile(context, sourceuri, getMimeType(name));
} else {
input = context.getContentResolver().openInputStream(sourceuri);
}
boolean directorySetupResult;
File destDir = new File(destinationDir);
if (!destDir.exists()) {
directorySetupResult = destDir.mkdirs();
} else if (!destDir.isDirectory()) {
directorySetupResult = replaceFileWithDir(destinationDir);
} else {
directorySetupResult = true;
}
if (!directorySetupResult) {
hasError = true;
} else {
String destination = destinationDir + File.separator + destFileName;
int originalsize = input.available();
bis = new BufferedInputStream(input);
bos = new BufferedOutputStream(new FileOutputStream(destination));
byte[] buf = new byte[originalsize];
bis.read(buf);
do {
bos.write(buf);
} while (bis.read(buf) != -1);
}
} catch (Exception e) {
e.printStackTrace();
hasError = true;
} finally {
try {
if (bos != null) {
bos.flush();
bos.close();
}
} catch (Exception ignored) {
}
}
return !hasError;
}
private static boolean replaceFileWithDir(String path) {
File file = new File(path);
if (!file.exists()) {
if (file.mkdirs()) {
return true;
}
} else if (file.delete()) {
File folder = new File(path);
if (folder.mkdirs()) {
return true;
}
}
return false;
}
Call this method from an AsycTask. Let me know if this helps.
private static String FILE_NAM = "video";
String outputfile = getFilesDir() + File.separator+FILE_NAM+"_tmp.mp4";
InputStream in = getContentResolver().openInputStream(videoFileUri);
private static File createFileFromInputStream(InputStream inputStream, String fileName) {
try {
File f = new File(fileName);
f.setWritable(true, false);
OutputStream outputStream = new FileOutputStream(f);
byte buffer[] = new byte[1024];
int length = 0;
while((length=inputStream.read(buffer)) > 0) {
outputStream.write(buffer,0,length);
}
outputStream.close();
inputStream.close();
return f;
} catch (IOException e) {
System.out.println("error in creating a file");
e.printStackTrace();
}
return null;
}
I have used following code to save a file from an existing Uri given back from an Intent to an Uri that my App hosts:
private void copyFile(Uri pathFrom, Uri pathTo) throws IOException {
try (InputStream in = getContentResolver().openInputStream(pathFrom)) {
if(in == null) return;
try (OutputStream out = getContentResolver().openOutputStream(pathTo)) {
if(out == null) return;
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
}
}
}
Here's the easiest and the cleanest:
private void saveFile(Uri sourceUri, File destination)
try {
File source = new File(sourceUri.getPath());
FileChannel src = new FileInputStream(source).getChannel();
FileChannel dst = new FileOutputStream(destination).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
When receiving a android.net.Uri from an external source, the best way to save the file is from the stream:
try (InputStream ins = activity.getContentResolver().openInputStream(source_uri)) {
File dest = new File(destination_path);
createFileFromStream(ins, dest);
} catch (Exception ex) {
Log.e("Save File", ex.getMessage());
ex.printStackTrace();
}
createFileFromStream method:
public static void createFileFromStream(InputStream ins, File destination) {
try (OutputStream os = new FileOutputStream(destination)) {
byte[] buffer = new byte[4096];
int length;
while ((length = ins.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
os.flush();
} catch (Exception ex) {
Log.e("Save File", ex.getMessage());
ex.printStackTrace();
}
}
1.Create a file from a URI path as:
File from = new File(uri.toString());
2.Create another File where you want the file to save as:
File to = new File("target file path");
3.Rename the file as:
from.renameTo(to);
With this the file from default path is automatically deleted and created at the new path.
How to get external storage location and save a file
This answer is not for the question, but for the title.
It took hours to figure out how to do this since no article explains the process totally, while some of them are years old and uses deprecated APIs. Hope this might be helpful for future developers.
Get location of External Storage
For instance, from inside a fragment,
// when user choose file location
private val uriResult = registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri ->
// do when user choose file location
createFile(uri)
}
fun openFileChooser() {
// startActivityForResult() is deprecated
val suggestedFileName = "New Document.txt"
uriResult.launch(suggestedFileName)
}
Write file data using Uri
It may seem difficult to create a java.io.File from an android.net.Uri, since there is no direct way to convert an android.net.Uri into java.net.URI. But if you have the ApplicationContext you can do it very easily.
fun createFile(uri: Uri) {
try {
requireContext().applicationContext.contentResolver.openFileDescriptor(uri, "w")?.use { fd ->
FileOutputStream(fd).use { fos ->
// do your job on the FileOutputStream
// also use background thread
fos.close()
}
}
} catch (e: Exception) {
}
}
Note: File operations throws multiple exceptions, so handle them carefully. And also do file operations in worker threads.
I slightly modified Sebi's answer to work for Kotlin:
fun copyUri(context: Context, pathFrom: Uri, pathTo: Uri?) {
context.contentResolver.openInputStream(pathFrom).use { inputStream: InputStream? ->
if (pathTo == null || inputStream == null) return
context.contentResolver.openOutputStream(pathTo).use { out ->
if (out == null) return
// Transfer bytes from in to out
val buf = ByteArray(1024)
var len: Int
while (inputStream.read(buf).also { len = it } > 0) {
out.write(buf, 0, len)
}
}
}
}
You can do it using
new File(uri.getPath());
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.