Reading files from a ZIP file in your Android assets folder - android

I'm reading files from a ZIP file that's located in my Android assets folder using ZipInputStream: it works, but it's really slow, as it has to read it sequentially using getNextEntry(), and there are quite a lot of files.
If I copy the ZIP file onto the SD card, reading is really fast when using ZipFile.getEntry, but I didn't find a way to use ZipFile with the asset file!
Is there any way to access the ZIP in the asset folder in a speedy way? Or do I really have to copy the ZIP to the SD card?
(BTW, in case anybody wonders why I'm doing this: the app is larger than 50 MB, so in order to get it in the Play Store I have to use Expansion APKs; however, as this app should also be put into the Amazon App Store, I have to use another version for this, as Amazon doesn't support Expansion APKs, naturally... I thought that accessing a ZIP file at two different locations would be an easy way to handle this, but alas...)

This works for me:
private void loadzip(String folder, InputStream inputStream) throws IOException
{
ZipInputStream zipIs = new ZipInputStream(inputStream);
ZipEntry ze = null;
while ((ze = zipIs.getNextEntry()) != null) {
FileOutputStream fout = new FileOutputStream(folder +"/"+ ze.getName());
byte[] buffer = new byte[1024];
int length = 0;
while ((length = zipIs.read(buffer))>0) {
fout.write(buffer, 0, length);
}
zipIs.closeEntry();
fout.close();
}
zipIs.close();
}

You can store the uncompressed files directly in assets (i.e. unpack the zip into assets/ folder). This way, you can access the files directly and they will be compressed anyway when you build the APK.

You can create a ZipInputStream in the following way :
ZipInputStream zipIs = new ZipInputStream(context.getResources().openRawResource(your.package.com.R.raw.filename));
ZipEntry ze = null;
while ((ze = zipIs.getNextEntry()) != null) {
FileOutputStream fout = new FileOutputStream(FOLDER_NAME +"/"+ ze.getName());
byte[] buffer = new byte[1024];
int length = 0;
while ((length = zipIs.read(buffer))>0) {
fout.write(buffer, 0, length);
}
zipIs .closeEntry();
fout.close();
}
zipIs .close();

Related

Error while creating a new file in Android: FileNotFoundException: /test.png: open failed: EROFS (Read-only file system)

I have a png file in the raw folder. I get the inputStream using :
inputStream = getResources().openRawResource(R.raw.test);
I am trying to write this inputStream in a new file in an Android application. This is my code:
inputStream = getResources().openRawResource(R.raw.test);
File file = new File("/test.png");
outputStream = new FileOutputStream(file);
int read = 0;
byte[] bytes = new byte[1024*1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
outputStream.close();
inputStream.close();
When I run the application, I get the following error in the logcat:
java.io.FileNotFoundException: /test.png: open failed: EROFS (Read-only file system)
Basically I want to create a File object so that I can send this to my server.
Thank you.
You will not have access to the file-system root, which is what you're attempting to access. For your purposes, you can write to internal files new File("test.png"), which places the file in the application-internal storage -- better yet, access it explicitly using getFilesDir().
For truly temporary files, you might want to look into getCacheDir() -- should you forget to delete those temporary files, the system will reclaim the space when it runs out of room.
Here's my solution:
inputStream = getResources().openRawResource(R.raw.earth);
file = new File(Environment.getExternalStorageDirectory() + File.separator + "test.png");
file.createNewFile();
outputStream = new FileOutputStream(file);
int read = 0;
byte[] bytes = new byte[1024*1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
outputStream.close();
inputStream.close();

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

Android Zip file access

is there any way how to open zip file and access it as it was unzipped in Android? I have zip file with data, which I need to be accessed by application and I don't want to unzip it to some folder and open it from there. So I want data access directly from zip file without unzipping it. I am now trying to use ZipFile, but I haven't found any usage, which might be usefull for me. Is this approach even possible?
Thanks
This is the code I've been using to 'unzip on the fly' and create drawables.
private void createBitmapDrawables() throws IOException {
// TODO Auto-generated method stub
InputStream is = getResources().getAssets().open("images.zip");
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
try {
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int count;
while ((count = zis.read(buffer)) != -1) {
baos.write(buffer, 0, count);
}
String file = ze.getName();
byte[] bytes = baos.toByteArray();
drawable = createBitmapDrawable(bytes, file);
}
} finally {
zis.close();
}
}
Pretty much from the Android Dev site.
Hope it helps.
Well as every file, in order to use its content you have to open and read it in memory and typically at this purpose you use an InputStream. In this case you have to read an entry of a zip file so you can iterate trough the zip entry and when you find the entry you are looking for you can obtain a ZipInputStream for it and read like a normal file

Write media file to internal/external storage

I am currently trying to figure out a way to write a media file to internal/external storage (primary storage). The file to be saved could be any size from a few MBs to 50MBs. I have logic that works on my Droid X 2.3.3 Razr 2.3.5 (I believe) but does not work on my Galaxy Nexus (has no removable storage but a built in 16Gig card with v4.0.2). I have looked around and haven't found any code/samples that work with v4.0. Maybe I am approaching this all wrong since it doesn't have an actual sd card? maybe it is something new in v4.0? Currently when I run my application on the Galaxy Nexus I get this: System.err(19520): java.io.FileNotFoundException:
UPDATED
InputStream inputStream = urlConnection.getInputStream();
File PATH = Environment.getExternalStorageDirectory();
File FILE = new File(Environment.getExternalStorageDirectory()+ "/" + FILENAME);
ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
// buffer int bufferSize = 1024; int bufferLength = 0; byte[] buffer = new byte[bufferSize];
while ((bufferLength = inputStream.read(buffer)) != -1) {
byteBuffer.write(buffer, 0, bufferLength);
}
byte[] temp = byteBuffer.toByteArray();
FileOutputStream fos = new FileOutputStream(FILE);
fos.write(temp);
fos.close();
Are you putting this file in a specific directory on your sdcard?external storage?
I assume your permissions in your manifest are good because the 'permission denied is not raised' so maybe if you put the file in a specific folder, which is not created you should call the mkdirs() function on your file!
First, don't convert it to a string, just use getExternalStorageDirectory() as a File:
File sd = Environment.getExternalStorageDirectory();
File file = new File(sd, FILENAME);
I don't know if that will correct it or not, but it wouldn't hurt to try that. And you do not need to call file.createNewFile() before writing to the file with a FileOutputStream. The docs about FileOutputStream say:
An output stream that writes bytes to a file. If the output file
exists, it can be replaced or appended to. If it does not exist, a new
file will be created.
And which line of code is the FileNotFoundException happening on?

Android: read a GZIP file in the ASSETS folder

How can you read GZIP file in Android located in the "ASSETS" (or resources/raw) folder?
I have tried the following code, but my stream size is always 1.
GZIPInputStream fIn = new GZIPInputStream(mContext.getResources().openRawResource(R.raw.myfilegz));
int size = fIn.available();
for some reason the size is always 1. But if Idon't GZIP the file, it works fine.
NOTE:
Using Android 1.5
I met the same problem when reading a gz file from assets folder.
It's caused by the file name of the gz file. Just renaming yourfile.gz to other name like yourfile.bin. It seems Android build system would decompress a file automatically if it thought it's a gz.
public class ResLoader {
/**
* #param res
* #throws IOException
* #throws FileNotFoundException
* #throws IOException
*/
static void unpackResources() throws FileNotFoundException, IOException {
final int BUFFER = 8192;
android.content.res.Resources t = TestingE3d.mContext.getResources();
InputStream fis = t.openRawResource(R.raw.resources);
if (fis == null)
return;
ZipInputStream zin = new ZipInputStream(new BufferedInputStream(fis,
BUFFER));
ZipEntry entry;
while ((entry = zin.getNextEntry()) != null) {
int count;
FileOutputStream fos = TestingE3d.mContext.openFileOutput(entry
.getName(), 0);
BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);
byte data[] = new byte[BUFFER];
while ((count = zin.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, count);
// Log.v("NOTAG", "writing "+count + " to "+entry.getName());
}
dest.flush();
dest.close();
}
zin.close();
}
}
R.raw.resources is a zip file - this class will decompress all files in that zip to your local folder.
I use this for NDK.
you can access your fils from ndk through:
/data/data//files/
package = package where ResLoader resides
filename = one of files that is in raw/resources.zip
this is the documented behavior of InflaterInputStream.available:
http://java.sun.com/javase/6/docs/api/java/util/zip/InflaterInputStream.html#available()
Returns 0 after EOF has been reached, otherwise always return 1.
abusing available is a common mistake --- in no case can you assume that it tells you the length of a file (though it sometimes happens to do so, as you've noticed). you want to keep calling read(byte[], int, int) until it returns 0. if you want the length to allocate a byte[] up front, you probably want to create a ByteArrayOutputStream and write to that each time you read, and then get a byte[] from that when you exit the loop. this works for all InputStreams in all cases.
It seems that the build system treats .gz files as a special case, even when it's included as a raw resource. Rename the .gz file to have a different extension, say .raw or .bin .
Valid at least for Android Studio 2.2 . I can't find any docs to confirm this is expected behaviour or, better, how to prevent it, but changing the extension at least works around the problem.
What happens if you use AssetManager instead of Resources? Example:
InputStream is = mContext.getAssets().open("myfilegz");
GZIPInputStream fIn = new GZIPINputStream(is);
Internally, Resources is just calling AssetManager; I wonder if somewhere along the way it musses things up.
Try looking at the source for Translate from apps-for-android open source project and see if that helps at all.
They use GZIPInputStream on a raw file in their selectRandomWord() function [line 326] (source pasted below)
public void selectRandomWord() {
BufferedReader fr = null;
try {
GZIPInputStream is =
new GZIPInputStream(getResources().openRawResource(R.raw.dictionary));

Categories

Resources