Writing to the file system is taking too long - android

I am trying to download around 300-400 image files using the optimal number of threads(around 20-30) and then writing these images into the file system of Android. The problem is that the whole process is taking 10-13 minutes and I want to clock it around 2-3 minutes with high-speed internet
I think this can be achieved as the size of those 300-400 image files is in some KBs only(total size would be around 80-100 MB)
Here's my code for downloading and saving file
long t1 = System.currentTimeMillis();
Call<ResponseBody> call=apiService.fetchAttachmentDetail(att_id,token,true,device_id, 0.0f, 0.0f);
Response<ResponseBody> response = call.execute();
long t2 = System.currentTimeMillis();
Help.E("downloaded " + att_id + "and time taken in mili second " + (t2 - t1));
Help.E("content length- " + response.body().contentLength());
String str = response.headers().get("Content-Type");
if (str == null)
str = "/png";
String ext_type = str.substring(str.indexOf("/") + 1);
InputStream in = null;
FileOutputStream out = null;
ContextWrapper cw = new ContextWrapper(context);
String fileName = "";
File directory = cw.getDir(AttachmentDirName, Context.MODE_PRIVATE);
try {
in = response.body().byteStream();
fileName = String.valueOf(att_id) + "." + ext_type;
File file = new File(directory, fileName);
out = new FileOutputStream(file);
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
} catch (IOException e) {
Log.d("Error", e.toString());
emitter.onError(e);
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
long t3 = System.currentTimeMillis();
Help.E("saved " + att_id + "time taken in saving the file " + (t3 - t2));

You are reading and writing your data byte by byte, it takes significantly more time compared with reading and writing with a buffer. Try to use this method:
public static void copyStream(InputStream input, OutputStream output)
throws IOException
{
byte[] buffer = new byte[1024]; // Adjust if you want
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1)
{
output.write(buffer, 0, bytesRead);
}
}
So you replace your
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
with
copyStream(in, out);

Related

How to programatically download files with increased speed in android

Hi have implemented programatically downloading of file using inputstream and cipheroutputstream(for encryption). The download is happening very slow. Whereas if i try to download via download manager, it is very fast. What can i do to improve my code and increase the download speed of the file. Below is my code.
private void saveFileUsingEncryption(String aMineType, long length) throws Exception {
int bufferSize = 1024*4;
//byte[] buffer = new byte[1024];
byte[] buffer = new byte[bufferSize];
int bytesRead = 0;
long totalRead = 0;
FileOutputStream outStream = null;
File f = new File(Constants.DWLPATH);
if (!f.exists()) {
f.mkdirs();
}
try {
Cipher aes = Cipher.getInstance("ARC4");
aes.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("mykey".getBytes(), "ARC4"));
if(contDisp==null || contDisp.length()==0) {
// downloadFileName = downloadFileName.replaceAll("[^a-zA-Z0-9_]+", "");
downloadFileName = downloadFileName + "." + getFileExtension(aMineType);
}
outStream = new FileOutputStream(Constants.DWLPATH + downloadFileName,true);
CipherOutputStream out = new CipherOutputStream(outStream, aes);
while ((bytesRead = inputStream.read(buffer, 0, bufferSize)) >= 0) {
out.write(buffer, 0, bytesRead);
try{
// Adjust this value. It shouldn't be too small.
Thread.sleep(50);
}catch (InterruptedException e){
TraceUtils.logException(e);
}
totalRead += bytesRead;
sb=sb.append("\n Total bytes Read:"+totalRead);
Log.e("--",sb.toString());
/* if (this.length > 0) {
Long[] progress = new Long[5];
progress[0] = (long) ((double) totalRead / (double) this.length * 100.0);
publishProgress(progress);
}*/
if (this.isCancelled()) {
if (conn != null)
conn.disconnect();
conn = null;
break;
}
}
Log.e("Download completed","success");
out.flush();
//Utils.putDownloadLogs(requestUrl,mimeType,length, downloadFileName,"Download is Successful",sb.toString(), context);
outStream.close();
buffer = null;
} catch (Exception e) {
TraceUtils.logException( e);
file_newsize = storedFileSizeInDB + totalRead;
if (totalFileSize == 0)
totalFileSize = length;
callback.onRequestInterrupted(file_newsize,totalFileSize);
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
// Utils.putDownloadLogs(requestUrl,mimeType,length,downloadFileName,"failure---" + errors.toString(),sb.toString(), context);
throw e;
} finally {
if (outStream != null)
outStream.close();
outStream = null;
}
}
You can use default download manager to download the file because its very easy to implement and provide better features like respond to the internet connection , provide accessibility to add notification in status bar , by running the query on download manager object you can find the total bytes and remaining bytes so you can calculate the progress and after completion of download by tapping the notification one can perform the desired operation.
And also there are many libraries are available for to achieve this like
PRDOWNLOADER
FetchDownloader
This libraires provide you the feature of pause,download, resume download , tracking the progress and cancel download
Also you can customize it as per your need.
Here is the DownloadAndEncryptFileTask.class to download with encryption
public class DownloadAndEncryptFileTask extends AsyncTask<Void, Void, Void> {
private String mUrl;
private File mFile;
private Cipher mCipher;
InputStream inputStream;
FileOutputStream fileOutputStream;
CipherOutputStream cipherOutputStream;
public DownloadAndEncryptFileTask(String url, File file, Cipher cipher) {
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("You need to supply a url to a clear MP4 file to download and encrypt, or modify the code to use a local encrypted mp4");
}
mUrl = url;
mFile = file;
mCipher = cipher;
}
private void downloadAndEncrypt() throws Exception {
URL url = new URL(mUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if (mFile.length() > 0) {
connection.setRequestProperty("Range", "bytes=" + mFile.length() + "-");
}
connection.connect();
Log.e("length", mFile.length() + "");
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new IOException("server error: " + connection.getResponseCode() + ", " + connection.getResponseMessage());
}
inputStream = connection.getInputStream();
if (mFile.length() > 0) {
//connection.connect();
fileOutputStream = new FileOutputStream(mFile, true);
} else {
fileOutputStream = new FileOutputStream(mFile);
}
CipherOutputStream cipherOutputStream = new CipherOutputStream(fileOutputStream, mCipher);
byte buffer[] = new byte[1024 * 1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
Log.d(getClass().getCanonicalName(), "reading from http...");
cipherOutputStream.write(buffer, 0, bytesRead);
}
inputStream.close();
cipherOutputStream.close();
connection.disconnect();
}
#Override
protected Void doInBackground(Void... params) {
try {
downloadAndEncrypt();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
Log.d(getClass().getCanonicalName(), "done");
}
}
Call this class
new DownloadAndEncryptFileTask(
myFeedsModel.getVideo().getVideo360(),
new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), myFeedsModel.getFile_name()),
OBJECT OF YOUR CIPHER

Android:How to Copy a Folder From 'assets' Includes files and folders to sdcard [duplicate]

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());
}
}

Corrupt audio file after copying over bluetooth stream

I am sending a audio file by sending it over a bluetooth socket connection to another device. I am copying byte for byte, so the size on the other end is exactly the same.
However, when I try to play the file on the other end, I just get awkward noises (each time i start the player it sounds a little different).
Now the strange part: As soon as I copy the "corrupt" file to another location (via some android file manager), I can play it and it sounds totally fine!
Any ideas?
sending:
outputStream = mSocket.getOutputStream();
long totalLength = file.length();
String command = Protocol.COMMAND_SEND_FILE + Protocol.SEPARATOR + file.getName() + Protocol.SEPARATOR + totalLength;
outputStream.write(command.getBytes());
outputStream.flush();
long bytesWritten = 0;
while ((c = is.read(buffer, 0, buffer.length)) > 0) {
outputStream.write(buffer, 0, c);
outputStream.flush();
bytesWritten += c;
}
mLogger.log("sent total of bytes", bytesWritten+"");
is.close();
receiving:
while (mRunning) {
final StringBuilder sb = new StringBuilder();
if (!mReceiveFile) {
bytesRead = mInputStream.read(buffer);
if (bytesRead != -1) {
String result = "";
while ((bytesRead == bufferSize) && (buffer[bufferSize] != 0)) {
result = result + new String(buffer, 0, bytesRead);
bytesRead = mInputStream.read(buffer);
}
result = result + new String(buffer, 0, bytesRead);
sb.append(result);
}
} else {
if (mLogger != null) {
mLogger.log("receiving file", mReceiveFilename);
}
int c = 0;
long bRead = 0;
OutputStream oos = new FileOutputStream(Utils.getAppRootDir() + "/" + mReceiveFilename);
while (bRead < mReceiveFileLength && (c = mInputStream.read(buffer, 0, buffer.length)) > 0) {
if ((bRead + bufferSize) >= mReceiveFileLength) {
c = (int) (mReceiveFileLength - bRead);
mLogger.log("rest bytes", "" + c);
}
if(bRead < 10000 || bRead + 10000 > mReceiveFileLength){
mLogger.log(new String(buffer));
}
oos.write(buffer, 0, c);
oos.flush();
bRead += c;
mLogger.log("read " + bRead + " of " + mReceiveFileLength + " bytes");
}
oos.close();
mLogger.log("saved file", mReceiveFilename);
mReceiveFile = false;
mReceiveFilename = null;
}
if (sb.toString().startsWith(Protocol.COMMAND_SEND_FILE)) {
// "SEND_FILE:filename.ext"
try {
String[] command = sb.toString().split(Protocol.SEPARATOR);
mReceiveFile = true;
mReceiveFilename = command[1];
mReceiveFileLength = Long.parseLong(command[2]);
} catch (Exception e) {
mLogger.log("Protocoll exception command could not be parsed:" + sb.toString());
}
} else {
EventBus.getDefault().post(new BluetoothCommunicator(sb.toString()));
}
}
I figured out that I have to start the media scanner after creating the new file. After that it plays like a charm!
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));

Download resume not working in setRequestProperty method

This is my doInBackground method:
#Override
protected String doInBackground(String... sUrl) {
InputStream input = null;
HttpURLConnection conection = null;
BufferedOutputStream bout = null;
FileOutputStream fos = null;
int downloaded = 0;
try {
URL url = new URL(sUrl[0]);
conection = (HttpURLConnection)url.openConnection();
int lenghtOfFile = conection.getContentLength();
if(STATUS) {
File file = new File(Environment.getExternalStorageDirectory().getPath() + "/myapp.apk");
if (file.exists()) {
downloaded = (int) file.length();
conection.setRequestProperty("Range", "bytes=" + (file.length()) + "-");
}
}
else {
conection.setRequestProperty("Range", "bytes=" + downloaded + "-");
}
conection.setDoInput(true);
conection.setDoOutput(true);
conection.connect();
input = new BufferedInputStream(url.openStream(), 8192);
fos=(downloaded==0)? new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + "/myapp.apk"): new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + "/myapp.apk",true);
bout = new BufferedOutputStream(fos, 1024);
byte data[] = new byte[1024];
long total = 0;
int count = 0;
while ((count = input.read(data, 0, 1024)) >= 0) {
if (isCancelled()) {
input.close();
return null;
}
bout.write(data, 0, count);
downloaded += count;
publishProgress((int)(downloaded * 100/ lenghtOfFile) );
total += count;
}
bout.flush();
input.close();
fos.close();
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
if (fos != null)
fos.close();
if (bout != null)
bout.close();
} catch (IOException ignored) {
}
if (conection != null)
conection = null;
}
return null;
}
I start download task with this code (resume flag is false -> STATUS = FALSE):
dt = new DownloadTask(DownloadsActivity.this, false);
dt.execute("myurl.something.apk");
then when downloaded completely I launch apk file and all thing work correctly and apk installed correctly. But when pause my download with this code:
dt.cancel(true);
and then resume it with this code (resume flag is true-> STATUS = TRUE):
dt = new DownloadTask(DownloadsActivity.this, true);
dt.execute("myurl.something.apk");
This time apk size is equal to last downloaded before pause + apk total size, therefore my apk file is corrupted. Which means connection.setRequestProperty() not working for me. What is my code problem? Thanks in advance.

Copy directory from Assets to local directory

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());
}
}

Categories

Resources