I am trying to transfer a SQLite database into an app by downloading it and then unzipping it to the correct location. I was successful in transferring the DB when it was unzipped. The error I get is that it cannot find any of the tables I query. I have also been successful in unzipping and reading normal text files.
The DB has Hebrew and English, but that has not caused problems before. The bilingual DB was copied successfully when it was not zipped and bilingual texts have been successfully unzipped and read. Still, it is a possibility that there is an encoding problem going on. That seems weird to me, because as you can see below in the code, I'm just copying the bytes directly.
-EDIT-
Let's say the prezipped db is called test1.db. I zipped it, put it in the app, unzipped it and called that test2.db. when I ran a diff command on these two, there were no differences. So there must be a technical issue with the way android is reading the file / or maybe encoding issue on android that doesn't exist on pc?
I hate to do a code dump, but i will post both my copyDatabase() function (which works). That is what I used previously running it on an unzipped DB file. I put it here as comparison. Now I'm trying to use unzipDatabase() function (which doesn't work), and use it on a zipped DB file. The latter function was copied from How to unzip files programmatically in Android?
private void copyDatabase() throws IOException{
String DB_NAME = "test.db";
String DB_PATH = "/data/data/org.myapp.myappname/databases/";
//Open your local db as the input stream
InputStream myInput = myContext.getAssets().open(DB_NAME);
// Path to the just created empty db
String outFileName = DB_PATH + DB_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();
}
private boolean unzipDatabase(String path)
{
String DB_NAME = "test.zip";
InputStream is;
ZipInputStream zis;
try
{
String filename;
is = myContext.getAssets().open(DB_NAME);
zis = new ZipInputStream(is);
ZipEntry ze;
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null)
{
// write to a file
filename = ze.getName();
// Need to create directories if not exists, or
// it will generate an Exception...
if (ze.isDirectory()) {
Log.d("yo",path + filename);
File fmd = new File(path + filename);
fmd.mkdirs();
continue;
}
OutputStream fout = new FileOutputStream(path + filename);
// reading and writing zip
while ((count = zis.read(buffer)) != -1)
{
fout.write(buffer, 0, count);
}
fout.flush();
fout.close();
zis.closeEntry();
}
zis.close();
}
catch(IOException e)
{
e.printStackTrace();
return false;
}
return true;
}
So still don't know why, but the problem is solved if I first delete the old copy of the database (located at DB_PATH + DB_NAME) and then unzip the new one there. I didn't need to do this when copying it directly.
so yay, it was a file overwriting issue...If someone knows why, feel free to comment
Related
Have a Word DOCX file stored in a table (zip file). With Java stored procedure, I like to replace some string inside "word/document.xml" file and store all files into new archive. If I run the code on Oracle database I get corrupted zip file. File size of ouptut file is smaller than input file...
If I run the same app on client (Netbeans IDE) then all works fine. I can't find the where is the problem!?
public static void zamenjajVsebino(oracle.sql.BLOB srcBlob1, oracle.sql.BLOB[] dstBlob) throws Exception {
InputStream zipBuffer = srcBlob1.getBinaryStream();
OutputStream outBuffer = dstBlob[0].getBinaryOutputStream();
ZipInputStream zipIn = new ZipInputStream(zipBuffer);
ZipOutputStream zipOut = new ZipOutputStream(outBuffer);
ZipEntry inEntry;
while ((inEntry = zipIn.getNextEntry()) != null) {
ZipEntry outEntry = new ZipEntry(inEntry.getName());
zipOut.putNextEntry(outEntry);
if (inEntry.getName().equals("content.xml") | inEntry.getName().equals("word/document.xml")) {
String contentIn = new String(getStringByte(zipIn), "UTF-8");
contentIn = contentIn.replaceAll("%TEXT%", "BLA BLA");
zipOut.write(contentIn.getBytes(), 0, contentIn.getBytes().length);
} else {
copy(zipIn, zipOut);
}
zipOut.closeEntry();
}
zipIn.close();
zipOut.flush();
zipOut.finish();
}
public static void copy(InputStream in, OutputStream out) throws IOException {
int n;
byte[] buffer = new byte[1024];
while ((n = in.read(buffer)) > -1) {
out.write(buffer, 0, n); // Don't allow any extra bytes to creep in, final write
}
out.flush();
}
If I open the source DOCX file is like this:
The output file is like this:
Here is my plsql source code:
-- select source docx file from blob...
select vdb.vsebina
into SrcBlobLocator
from dok_vsebina_dokumenta_blob vdb
where id=p_vdb_id;
-- prepare output blob
p_vdb_id_out:=dok_lob.f_pripravi_blob;
update dok_vsebina_dokumenta_blob set
naziv_datoteke='test.docx',
vsebina = empty_blob()
where id=p_vdb_id_out;
--select output blob for update...
select vdb.vsebina
into DstBlobLocator
from dok_vsebina_dokumenta_blob vdb
where id=p_vdb_id_out for update;
-- call java stored procedure with source and dest blob...
ZamenjajVsebino(SrcBlobLocator, DstBlobLocator);
I have a rather large database that ships with my app. I'm having a lot of trouble getting it to properly create itself on the local device, so I figure with the issues and it being so large, it might make more sense to host it on a server and work with it from there.
The database is 40 MB or less. What is the best way to manage this with it hosted somewhere?
The problem is with Database file size. You can make a Zip of your Database file and then copy it to your local path from Assets.
Here is the link: Copy Db File From Assets
Now, in that code replace the copyDataBase() function with the below one.
private void copyDataBase() throws IOException {
try {
InputStream mInputStream = mContext.getAssets().open(DB_NAME_ZIP);
String outFileName = DB_PATH + DB_NAME_ZIP;
OutputStream mOutputStream = new FileOutputStream(outFileName);
byte[] buffer = new byte[1024];
int length;
while ((length = mInputStream.read(buffer)) > 0) {
mOutputStream.write(buffer, 0, length);
}
ZipFile mZipFile = new ZipFile(DB_PATH + DB_NAME_ZIP);
InputStream nInputStream = mZipFile.getInputStream(mZipFile.getEntry(DB_NAME));
OutputStream nOutputStream = new FileOutputStream(DB_PATH + DB_NAME);
while ((length = nInputStream.read(buffer)) > 0) {
nOutputStream.write(buffer, 0, length);
}
nOutputStream.flush();
nOutputStream.close();
nInputStream.close();
// Close the streams
mOutputStream.flush();
mOutputStream.close();
mInputStream.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
//Delete Zip file to minimize memory usage
final String mPath = DB_PATH + DB_NAME_ZIP;
final File file = new File(mPath);
if (file.exists())
file.delete();
}
}
Here DB_NAME_ZIP is the Database Zip file what you put in Assets folder like Android.zip but it actually contains Android.sqlite or Android.db.
I hope this one can help you.
When I try to decompress my .gz file and overwrite the db file I get the Unknown format (magic number 5153). Here is my code for the decompression and overwrite.
InputStream fIn = c.getAssets().open("MyContacts");
// Path to the just created empty db
String outFileName = DB_PATH + DB_NAME;
//Open the empty db as the output stream
FileOutputStream myOutput = new FileOutputStream(outFileName);
GZIPInputStream gz = new GZIPInputStream(fIn);
//transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[10246];
int length;
while ((length = gz.read(buffer, 0,buffer.length)) != -1){
myOutput.write(buffer, 0, length);
}
//Close the streams
gz.close();
myOutput.flush();
myOutput.close();
fIn.close();
It is very likely that the asset is being decompressed on the fly before it gets to your code. The aapt has its own strong ideas about how to handle compressed asset files. The behavior depends in part (in ways that are undocumented as far as I can tell) on the asset file names. You are much better off placing uncompressed files in the assets directory and letting aapt compress them for you. It generally does an excellent job and relieves you of having to worry about this kind of thing.
See the blog post that zapl's comment points to.
Your asset is not compressed (or is being transparently decompressed), since it's finding the "SQ" at the beginning of "SQLite format" at the beginning of your file.
I am shipping my Android application with an SQLite database (300 KB), and after what I read I need to move it in order to use it. In iOS you move the database from the app to the documents folder because you can't write to the app because it is signed. Is this the reason on Android as well? So back to my question. The following code does not copy the database correctly, it only copies some of it. Why so? Have I done something wrong here.
private final static String DB_NAME = "klb.sqlite";
dbPath = "/data/data/" + context.getPackageName() + "/databases/"; // setting this in constructor
(The lines above is private members, applies to both code examples below)
BufferedInputStream bis = new BufferedInputStream(context.getAssets().open(DB_NAME));
FileOutputStream fos = new FileOutputStream(dbPath + DB_NAME);
while (bis.read() != -1) {
fos.write(bis.read());
}
bis.close();
fos.flush();
fos.close();
However this works: (Sorry for being lazy about re-indenting)
InputStream inputStream = context.getAssets().open("klb.sqlite");
OutputStream outputStream = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
inputStream.close();
outputStream.close();
while (bis.read() != -1) {
fos.write(bis.read());
}
Look at that closely. Your calling read twice, but write once. You're effectively skipping every other byte.
Use Case
I need to package up our kml which is in a String into a kmz response for a network link in Google Earth. I would like to also wrap up icons and such while I'm at it.
Problem
Using the implementation below I receive errors from both WinZip and Google Earth that the archive is corrupted or that the file cannot be opened respectively. The part that deviates from other examples I'd built this from are the lines where the string is added:
ZipEntry kmlZipEntry = new ZipEntry("doc.kml");
out.putNextEntry(kmlZipEntry);
out.write(kml.getBytes("UTF-8"));
Please point me in the right direction to correctly write the string so that it is in doc.xml in the resulting kmz file. I know how to write the string to a temporary file, but I would very much like to keep the operation in memory for understandability and efficiency.
private static final int BUFFER = 2048;
private static void kmz(OutputStream os, String kml)
{
try{
BufferedInputStream origin = null;
ZipOutputStream out = new ZipOutputStream(os);
out.setMethod(ZipOutputStream.DEFLATED);
byte data[] = new byte[BUFFER];
File f = new File("./icons"); //folder containing icons and such
String files[] = f.list();
if(files != null)
{
for (String file: files) {
LOGGER.info("Adding to KMZ: "+ file);
FileInputStream fi = new FileInputStream(file);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(file);
out.putNextEntry(entry);
int count;
while((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
}
}
ZipEntry kmlZipEntry = new ZipEntry("doc.kml");
out.putNextEntry(kmlZipEntry);
out.write(kml.getBytes("UTF-8"));
}
catch(Exception e)
{
LOGGER.error("Problem creating kmz file", e);
}
}
Bonus points for showing me how to put the supplementary files from the icons folder into a similar folder within the archive as opposed to at the same layer as the doc.kml.
Update Even when saving the string to a temp file the errors occur. Ugh.
Use Case Note The use case is for use in a web app, but the code to get the list of files won't work there. For details see how-to-access-local-files-on-server-in-jboss-application
You forgot to call close() on ZipOutputStream. Best place to call it is the finally block of the try block where it's been created.
Update: To create a folder, just prepend its name in the entry name.
ZipEntry entry = new ZipEntry("icons/" + file);