Android: Faster way to read/write a ZipInputStream? - android

I am currently writing an application that reads a zip file in my assets folder which contains a bunch of images. I am using the ZipInputStream API to read the contents and then writing each file to my: Environment.getExternalStorageDirectory() directory. I have everything working but the first time the application is run writing the images to the storage directory is INCREDIBLY slow. It takes about about 5 minutes to write my images to disc. My code looks like this:
ZipEntry ze = null;
ZipInputStream zin = new ZipInputStream(getAssets().open("myFile.zip"));
String location = getExternalStorageDirectory().getAbsolutePath() + "/test/images/";
//Loop through the zip file
while ((ze = zin.getNextEntry()) != null) {
File f = new File(location + ze.getName());
//Doesn't exist? Create to avoid FileNotFoundException
if(f.exists()) {
f.createNewFile();
}
FileOutputStream fout = new FileOutputStream(f);
//Read contents and then write to file
for (c = zin.read(); c != -1; c = zin.read()) {
fout.write(c);
}
}
fout.close();
zin.close();
The process of reading the contents of the particular entry and then writing to it is VERY slow. I am assuming it is more to do with reading than writing. I've read that you can use a byte[] array buffer to speed up the process but this does not seem to work! I tried this but it only read part of the file...
FileOutputStream fout = new FileOutputStream(f);
byte[] buffer = new byte[(int)ze.getSize()];
//Read contents and then write to file
for (c = zin.read(buffer); c != -1; c = zin.read(buffer)) {
fout.write(c);
}
}
When I do that I only get about 600-800 bytes written. Is there a way to speed this up?? Have I implemented the buffer array incorrectly??

I found a much better solution which implements the BuffererdOutputStream API. My solution looks like this:
byte[] buffer = new byte[2048];
FileOutputStream fout = new FileOutputStream(f);
BufferedOutputStream bos = new BufferedOutputStream(fout, buffer.length);
int size;
while ((size = zin.read(buffer, 0, buffer.length)) != -1) {
bos.write(buffer, 0, size);
}
//Close up shop..
bos.flush();
bos.close();
fout.flush();
fout.close();
zin.closeEntry();
I managed to increase my load time from anywhere from an average of about 5 minutes to about 5 (depending on how many images are in the package). Hope this helps!

Try use http://commons.apache.org/io/
like:
InputStream in = new URL( "http://jakarta.apache.org" ).openStream();
try {
System.out.println( IOUtils.toString( in ) );
} finally {
IOUtils.closeQuietly(in);
}

Related

Download(copy?) a file from my res/raw folder to the default Android download location?

I am making a soundboard for practice and I want to give the user the ability to download the sound (that I have included in the app in the res/raw folder) onClick of a menu item but I can only find information about downloading from an internet url, not something that I already included in the apk.
What is the best way to do this? I would like to give them the option to save to an SD card also if this is possible. A point towards the correct class to use in the documentation would be great! I've been googling to no avail.
Thanks!
Try something like this:
public void saveResourceToFile() {
InputStream in = null;
FileOutputStream fout = null;
try {
in = getResources().openRawResource(R.raw.test);
String downloadsDirectoryPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
String filename = "myfile.mp3"
fout = new FileOutputStream(new File(downloadsDirectoryPath + filename));
final byte data[] = new byte[1024];
int count;
while ((count = in.read(data, 0, 1024)) != -1) {
fout.write(data, 0, count);
}
} finally {
if (in != null) {
in.close();
}
if (fout != null) {
fout.close();
}
}
}
I don't know about the raw but I did a similar thing in my app using the assets folder. My files are under the assets/backgrounds folder as you can probably guess from the code below.
You can modify this code and make it work for you (I know I will only have 4 files which is why I have i go from 0 to 4 but you can change this to whatever you want).
This code copies the file starting with prefix_ (like prefix_1.png, prefix_2.png, etc) to my cache directory but you can obviously change the extension, the filename or the path you would like to save the assets to.
public static void copyAssets(final Context context, final String prefix) {
for (Integer i = 0; i < 4; i++) {
String filename = prefix + "_" + i.toString() + ".png";
File f = new File(context.getCacheDir() + "/" + filename);
if (f.exists()) {
f.delete();
}
if (!f.exists())
try {
InputStream is = context.getAssets().open("backgrounds/" + filename);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
FileOutputStream fos = new FileOutputStream(f);
fos.write(buffer);
fos.close();
} catch (Exception e) {
Log.e("Exception occurred while trying to load file from assets.", e.getMessage());
}
}
}

Eclipse: save image from drawable resource

I have to save an image in to a gallery from a drawable resource with a button and I have use this code:
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher3);
//generate file
String SDdirectory = Environment.getExternalStorageDirectory().getPath();
File externalStorageDir = Environment.getExternalStorageDirectory();
File f = new File(externalStorageDir, "Bitmapname.png");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.PNG,0 , bos);
byte[] bitmapdata = bos.toByteArray();
try {
OutputStream os = new FileOutputStream (new File ("storage/sdcard0/iob"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
now the problem is that i save a file of 0kb... o.o
Thanks in advance .
I don't know, if there is a better solution, but this code works for me:
//at first I've imported the bitmap normally.
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.wall);
//generate file
File dir = new File ("/sdcard/foldername/");
File f = new File(dir, String.format("mybitmapname.png"));
//then write it to galery by adding this lines
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.PNG, 0 , bos);
byte[] bitmapdata = bos.toByteArray();
FileOutputStream fos = new FileOutputStream(f);
fos.write(bitmapdata);
fos.flush();
fos.close();
bos.close();
Please make sure you have added this line in your manifest.xml:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Try this:
OutputStream os = new FileOutputStream(new File("path/to/file"));
Beware, though. The way you copy data between the streams may easily lead to heap overflow if the resource is large. You should consider a smaller buffer reused as many times as needed to copy the whole data:
byte[] data = new byte[1024];
int len = 0;
while ((len = is.read(data)) > -1) {
os.write(data, 0, len);
}
Another consideration would be to move the whole copy operation to a separate thread (e.g. using AsyncTask) as not to block the UI thread. See the example here: http://developer.android.com/reference/android/os/AsyncTask.html
The file is File object that you want to write to.
BTW I will suggest to go for Apache Commons IO for doing file operations.
Refer -> this

How to unzip large zip file & write to SDCARD in less time

To unzip zip file I've used classes from package java.util.zip* by refering this and it works correctly however to unzip a file of 40MB it takes 59 seconds. When I tried same zip file on iPhone project (we are developing app for both platforms - Android & iPone & that have functionality to unzip zip file & save unzipped content to SDCARD-Android or document directory - iPhone ), it takes only 14 seconds. The iphone app uses ziparchive.
So my question are:
1.From above experiment it clears that unzipping & file write operation to SDCARD in Java consumes more time as compared to iPhone app, so I decided to use C/C++ level unzipping & file write operation using NDK.
Is this right choice ?
2.I have searched on google, stackoverflow & some suggested to use minizip but there no sufficient help on how to use minizip in android. Is anyboday have tried minizip for android ?
3.I also tried NDK development for libz to achieve my goal, as Libz is added in NDK but not getting how to use it. Is anybody tried libz in NDK?
4.Is there any other Framework in Java or C/C++ that unzip large zip file & write them to SDCARD in less time ?
Please help me.
Here is my Java Unzip Code :
public String unzip() {
String result;
try {
FileInputStream fin = new FileInputStream(this.filePath);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
Log.v("Unzip", "Unzipping " + ze.getName());
if (ze.isDirectory()) {
_dirChecker(ze.getName());
} else {
// Read 16 k at a time
byte[] buffer = new byte[16*1024];
int read;
FileOutputStream fout = new FileOutputStream(this.location+ "/" + ze.getName());
while ((read = zin.read(buffer)) != -1)
{
fout.write(buffer, 0, read);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
result = "success";
} catch (Exception e) {
Log.e("unzip", "unzip", e);
result = "failure";
}
return result;
}
Why don't you try this code.It works awesome
String zipname = "data.zip";
FileInputStream fis = new FileInputStream(zipname);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
System.out.println("Unzipping: " + entry.getName());
int size;
byte[] buffer = new byte[2048];
FileOutputStream fos = new FileOutputStream(entry.getName());
BufferedOutputStream bos = new BufferedOutputStream(fos, buffer.length);
while ((size = zis.read(buffer, 0, buffer.length)) != -1) {
bos.write(buffer, 0, size);
}
bos.flush();
bos.close();
}
zis.close();
fis.close();
}
All of the unzip code eventually ends up in zlib. There is no Java implementation of "deflate" compression in Android core libs.
The only reason java.util.Zip should be slower than a "native" unzip is if the file I/O is done badly, e.g. something is using really small buffers. Looking at the code linked from the question, this is exactly what's happening -- it's operating on individual bytes.
One of the comments on the solution provided a patch that uses a 4K buffer. Drop that in and see what happens to your performance.
Try to just write 40Mb file to SDCard and measure time spent.
(Almost) all free (or even paid) implementations of zip archive support libraries are based on the same zlib code, which takes most processing speed during the unzipping. Java code should be much slower than native one, so I'd suggest to try NDK unzipping. Also, trying to unzip archive with zero compression level will give you a guess how much time unzipping code takes and how much time is spend on just data copying.
public boolean unzip(String zipfilepath, String destinationdir) {
try {
FileInputStream fis = new FileInputStream(zipfilepath);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
System.out.println("Unzipping: " + entry.getName());
int size;
byte[] buffer = new byte[2048];
FileOutputStream fos = new FileOutputStream(destinationdir+ "/" + entry.getName());
BufferedOutputStream bos = new BufferedOutputStream(fos, buffer.length);
while ((size = zis.read(buffer, 0, buffer.length)) != -1) {
bos.write(buffer, 0, size);
}
bos.flush();
bos.close();
}
zis.close();
fis.close();
return true;
}catch(Exception e){
return false;
}
}
String zipfilepath = context.getFilesDir().getPath()+"/"+myfile.zip;
String destinationdir = context.getFilesDir().getPath();
unzip(zipfilepath, destinationdir);

BitmapFactory : "decode returned false" for internal image files extracted from zip?

Need to display images downloaded and extracted from a zip into the "/files" directory of the app. the images are getting in there properly as far as I can tell - I am able to extract them from the emulator and view/open them from my desktop. but every attempt, every variation of code I have found and tried so far has failed (Tag: skia / Text: --- decoder->decode returned false).
My latest construct, which does work for image files downloaded separately and uncompressed :
String imgFile = new File(getFilesDir(), "myImage.jpg").getAbsolutePath();
ImageView myImageView = new ImageView(this);
Bitmap bm = null;
try{
bm = BitmapFactory.decodeFile(imgFile);
myImageView.setImageBitmap(bm);
} finally{
mainLayout.addView(myImageView);
}
And here is the construct I am using to handle the zip extraction. I assume this is where the problem lies but I am clueless as to what I could possibly do differently and to what effect:
ZipInputStream zis = new ZipInputStream(fis);
BufferedInputStream in = new BufferedInputStream(zis, 8192);
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null){
File dest_file = new File(getFilesDir(), ze.getName());
FileOutputStream fout = new FileOutputStream(getFilesDir() + "/" + ze.getName());
BufferedOutputStream out = new BufferedOutputStream(fout, 8192);
byte b[] = new byte[1024];
int n;
while ((n = in.read(b,0,1024)) >= 0) {
out.write(b,0,n);
}
for (int c = zis.read(); c != -1; c = zis.read()) {
fout.write(c);
}
zis.closeEntry();
fout.close();
}
zis.close();
fis.close();
At a terrible standstill here. Appreciate any solutions/suggestions.

Copying Xml File From Res/Xml Folder to Device Storage

I'm trying to copy an xml file from the res/xml folder to the device storage but I'm really struggling on how to do this.
I know that the starting point is to get an InputStream to read the xml file. This is achieved by using this:
InputStream is = getResources().openRawResource(R.xml.xmlfile);
Eventually the output stream will be:
file = new File("xmlfile.xml");
FileOutputStream fileOutputStream = new FileOutputStream(file);
But I'm really struggling on how to read and copy all the information from the initial xml file correctly and accurately.
So far, I've tried using various InputStream and OutputStream to read and write (DataInputStream, DataOutputStream, OutputStreamWriter, etc.) but I still didn't managed to get it correctly. There are some unknown characters (encoding issue?) in the produced xml file. Can anyone help me on this? Thanks!
From res/xml you can't you have to put all files in your assets folder then use below code
Resources r = getResources();
AssetManager assetManager = r.getAssets();
File f = new File(Environment.getExternalStorageDirectory(), "dummy.xml");
InputStream is = = assetManager.open("fileinAssestFolder.xml");
OutputStream os = new FileOutputStream(f, true);
final int buffer_size = 1024 * 1024;
try
{
byte[] bytes = new byte[buffer_size];
for (;;)
{
int count = is.read(bytes, 0, buffer_size);
if (count == -1)
break;
os.write(bytes, 0, count);
}
is.close();
os.close();
}
catch (Exception ex)
{
ex.printStackTrace();
}
I think you should use the raw folder instead. Have a look at http://developer.android.com/guide/topics/resources/providing-resources.html.
You can also use this code:
try {
InputStream input = getResources().openRawResource(R.raw.XZY);
OutputStream output = getApplicationContext().openFileOutput("xyz.mp3", Context.MODE_PRIVATE);
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (Exception e) {
}
And when you need file use this code:
File k =getApplicationContext().getFileStreamPath("xyz.mp3");

Categories

Resources