I am trying to obtain only one file (I know its name) from very large zip archive. This archive include around 100000 files because I do not want find my file in loop. I think that must be some solution for this case, something like command on Linux.
unzip archive.zip myfile.txt
I wrote following code
try {
FileInputStream fin = new FileInputStream(rootDir+"/archive.zip");
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = new ZipEntry("myfile.txt");
FileOutputStream fout = new FileOutputStream(rootDir+"/buff/" + 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);
}
This code create new file in buff directory, but this file is empty!
Please, help me with this trouble!
Thanks for your time!
I'm fairly new to Java, but the API documentation contains a pretty reasonable amount of information for the standard Java libraries, including for java.util.zip. Going from there into the ZipFile Entry, you can scroll down to the method listing to find a method called getEntry. This seems to be the route you should start with!
EDIT: bear in mind that you will probably need to include the directory (e.g.: "dir\subdirF\subdirW\FileThatYouWant.txt") when making the call, since that seems to be the way the files are named when you go through one-by-one.
EDIT 2: a considerable wealth of information is available here: Compressing and Decompressing Data Using Java APIs, if you're willing to read a bit :D.
Subject to memory constraints, the only reasonable solution for you might be to use a ZipInputStream object, which AFAIK will require you to step through each ZipEntry in the archive (on average 50,000?), but will not require you to load the entire file into memory. As far as performance, I would guess manually stepping through would be just as efficient as any current implementation of this niche functionality.
Related
I'm sorry to take your time, so I'll try to make it short. Before all, I apologize if my English is wrong or if I'm making dumb mistake, English is not my mother tong. I'm sorry if the answer is silly I'm still not experimented with the making of android apps. I'm making a android application and I'm trying to store some files on a public folder on the external storage. I'm having an issue and I have no idea what might be happening.
The issue is so :
I have a file in the 'download' directory. Then I'm moving the file to my public folder, then I rename it (all this through code). Everything seems to happens fine, the file is correctly moved, is readable and writeable. The file is then visible through app like 'File commander'. Although, when I connect my phone with my computer the file is nowhere to be seen with nautilus (ubuntu folder app), even with the terminal (sudo ls -a doesn't change a thing).
String path = (Environment.getExternalStoragePublicDirectory("myAppFolder").getAbsolutePath() + "/inside_folder/" + fileToMove.getName())
File folder = new File(path);
folder.mkdir();
File thatFile = new File (folder.getAbsolutePath() + "/downloading");
if (thatFile.exist()) thatFile.delete();
try {
FileInputStream instream = new FileInputStream(fileToMove);
FileOutputStream outstream = new FileOutputStream(thatFile);
FileChannel inChannel = instream.getChannel();
FileChannel outChannel = outstream.getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
instream.close();
outstream.close();
outChannel.close();
inChannel.close();
} catch (IOException e1) {
e1.printStackTrace();
}
thatFile.renameTo(new File(folder.getAbsolutePath + "/coucou"));
For some reason, if the file is rename with 'file commander', the file will now be visible through nautilus. Also, if no other file than the one invisible named 'coucou' then, I will have no way to explore to myAppFolder/inside_folder/imafile/coucou , myAppFolder (and so inside_folder) being unreachable. How can I change that ? How can I make it visible without File Commander. Also, I wonder how can a file be invisible ... anyway thank you for your time and help :-).
edit: Rebooting the device clear the issue. Yet, if new file are created, the won't appear until new reboot (or for now it seems so). I don't want the user to do so each time. Does anyone know how to fix that ? Thank you :).
I saw this problem has been met many times, but strangely I was not able to find a solution.
I am trying to write a binary file to the SDcard. This is the source code:
private void saveDataLongs() {
try
{
ObjectOutputStream oos = new ObjectOutputStream(ctx.openFileOutput(Environment.getExternalStorageDirectory().getAbsolutePath()+"/longs.bin", ctx.MODE_WORLD_WRITEABLE));
for (int w=0; w<longCount; w++)
oos.writeLong(longs[w]);
oos.close();
}
catch(IOException e)
{ e.printStackTrace(); }
}
The Manifest contains
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
and I receive this error:
01-21 22:19:57.323: E/AndroidRuntime(13713): java.lang.RuntimeException: Unable to start activity ComponentInfo{it.ccc.ccc/it.ccc.ccc.Ccc}: java.lang.IllegalArgumentException: File /sdcard/longs.bin contains a path separator
From other posts I could understand that some functions are meant to write only in the private storage of the app, so they don't expect to manage directories and paths.
Is some one able to help me? Whall I use a different method to write the data to the sd, or just make some other action before doing it? I'm trying to write to the sdcard a simple binary file (btw it's a precalculated sequence of number, and I need to pass it to my PC and then move it back to the assets, so, if there is a different way to obtain this goal, it's ok anyway).
Thank you very much.
You say that you are trying to write to external storage, but you are calling openFileOutput(), which is for internal storage.
Change:
new ObjectOutputStream(ctx.openFileOutput(Environment.getExternalStorageDirectory().getAbsolutePath()+"/longs.bin", ctx.MODE_WORLD_WRITEABLE));
to:
new ObjectOutputStream(new FileOutputStream(new File(Environment.getExternalStorageDirectory(), "longs.bin")));
or, better yet, to:
new ObjectOutputStream(new FileOutputStream(new File(ctx.getExternalFilesDir(null), "longs.bin")));
I like CommonsWare's answer. I would simply like to add that if you ever DO want to go down a path, don't use /. Use File.separator. I don't think I've ever had any errors come up when simply using / but still.
So if you made a sub-folder called "To-dos" in the sdcard's directory, you would do something like the following:
new ObjectOutputStream(new File(Environment.getExternalStorageDirectory() + File.separator + "To-dos", "longs.bin"));
I have a big archive (zip in my case) with size ~100MB and with ~15000 files in it. I need to QUICKLY extract only one file form this archive.
I tried the next code:
final String zipPath = "archive.zip";
FileInputStream fin = new FileInputStream(zipPath);
in = new ZipInputStream(fin);
for (ZipEntry entry = in.getNextEntry(); entry != null; entry = in.getNextEntry()) {
if(entry.equals("file.name")){
//unzip this entry
break;
}
}
It works but too SLOW.
Is it some another possibility to find necessary file in archive? For example, on linux it extremally fast possible with command
unzip archive.zip myfile.name
In general, I need to find and decompress one file from some archive. It can be some another format... May be with another format it can be more easy.
You can use the libzip library.
I'm writing a simple budget app for myself, and I'm having trouble figuring out how to write to internal storage. I don't seem to be writing to the file properly, and I can't find any more in depth examples than the Data Storage article on developer.android.com
Basically, I'm trying to write a test float to the MyBalance file, then read it into balance. In my actual code I use try/catch statements around the file in/out operations, but I skipped them to make the code more readable.
float test = 55;
float balance;
byte[] buffer = null;
FileOutputStream fos = openFileOutput( "MyBalance", Context.MODE_PRIVATE );
fos.write(Float.floatToRawIntBits(balance));
fis.read(buffer); //null pointer
ByteBuffer b = ByteBuffer.wrap(buffer);
balance=b.getFloat();
That's the gist of it, anyone see what I'm doing wrong?
Edit:
Thanks for the reply, I went ahead and converted to/from String like you suggested, but I still don't think the file is being created. I have an if statement that reads from it if it exists in onResume() and it isn't being run. Lemme post some of my code.
Here's how I'm writing the file, (setbal is an EditText and balanceview is a TextView):
balance = Float.valueOf(setbal.getText().toString());
balanceview.setText(setbal.getText());
balstring = String.valueOf(balance);
for (int i = 0; i < balstring.length(); ++i)
try {
fos.write((byte)balstring.charAt(i));
} catch (IOException e) {
e.printStackTrace();
}
I check if the file exists in onResume() like so:
File file = new File("data/data/com.v1nsai.mibudget/balance.txt");
Is that where an internal file for that context would be stored?
So this isn't exactly what you asked for, but this is how I have it working for Strings, and it may be helpful to you to see. (You could box the primatives and toString them of course if you wanted to use this code.)
Writing
FileOutputStream fos = context.openFileOutput("savedstate.txt", 0);
for (int i = 0; i < out.length(); ++i)
fos.write((byte)out.charAt(i));
Reading
StringBuilder inb = new StringBuilder();
FileInputStream fis = this.mContext.openFileInput("savedstate.txt");
int ch;
while((ch = fis.read()) != -1)
inb.append((char)ch);
Update
One thought that springs to mind is that you may not want to trust using a File object with a hand typed full path to the file. Instead, just use the FileInputStream with the context object and a relative path like in my code, then see if you get a String back of some length or something like that, or an exception that the file doesn't exist.
If you are really curious of where the file is created, or want to see it with your own eyes, I believe you can browse to it on your phone through the file manager in DDMS.
One last thing, I would suggest moving the try/catch block outside of your writing loop. Since it is an identical task being repeated, there is no need for the overhead of that approach, though it is typically good practice to minimize the size of your try/catch blocks.
Ok really one last thing, if you want to use the File object with the full path, you might want to have the path be the following:
File file = new File("/data/data/com.v1nsai.mibudget/balance.txt");
The beginning slash may make all the difference.
The problem is this:
I make an internet connection to some url and receive an HttpResponse with an app_example.apk.
Then I want to create a file (an .apk)
in the sdcard with this data so that this downloaded application
can be installed later.
How can I convert the HttpResponse to an .apk file?
Let's clear some details:
I have to get this apk file through an internet connection to my server
I don't want to install this applications I receive on the sdcard
All of this has to be done in my code, I cannot use android market
I am currently writing to that file.
What I'm doing is converting the HttpResponse to a byte[ ],
then that byte[ ] is written to a file (an .apk) using an ObjectOutputStream.
Like this:
// byte[] appByteArray - already has the internet response converted in bytes
try {
file = new File(Environment.getExternalStorageDirectory()+"/"+appName+".apk");
file.createNewFile();
FileOutputStream stream = null;
stream = new FileOutputStream(file, false);
ObjectOutputStream objectOut =
new ObjectOutputStream(new BufferedOutputStream(stream));
objectOut.writeObject(appByteArray);
objectOut.close();
} catch (Exception e) {
e.printStackTrace();
}
In the end, the file is created
and has the received content.
When I try to install it,
through a VIEW intent (using the default installer)
I get a parse error saying that it could not find the AndroidManifest.xml.
I think that in some step along the way, the received data is being corrupted.
Do you have another method to solve this?
Many thanks
Don't use an ObjectOutputStream, byte array is serialized as Object, not written as raw data.
Are you sure that you have SD card write permission? android.permission.WRITE_EXTERNAL_STORAGE
Don't write into SD card root directory. Number of files in root dir can be limited. Instead create you app subdirectory on SD CARD.
This code works for me:
try {
String filePath = Environment.getExternalStorageDirectory()
+ "/myappdir/" + appName + ".apk";
File file = new File(filePath);
file.getParentFile().mkdirs();
file.createNewFile();
BufferedOutputStream objectOut = new BufferedOutputStream(
new FileOutputStream(file));
objectOut.write(appByteArray);
objectOut.close();
} catch (Exception e) {
e.printStackTrace();
}
This may not be the core problem, but I don't think you want to wrap stream in an ObjectOutputStream, since that is used for object serialization. It could be that it is adding extra data to the file so it can be deserialized with ObjectInputStream.
I would try pulling the apk off of the emulator (or device) and check it's MD5 versus the file on the server to make sure that the bits are being written out correctly.
Take a look at Pavel P's answer.
Also, I would note that your idea of installing the APK using the VIEW intent action does work, as I have tested this technique in the past.
However, unless the user has explicitly gone into Settings → Applications and selected "Allow non-Market applications", your installation will fail and the user will just see a screen telling them that for security reasons the installation has been blocked.
Basically you really need to rely on having fairly tech-savvy users who are willing to overlook a scary security warning and go and disable that setting.