In my activity I create an object to copy a database from asset folder to app database, everything works fine in emulator but in the device, I get no such file or directory Error on
OutputStream os = new FileOutputStream(dbFile);
I have permission needed:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
I call in MainActivity:
db = new ExternalDB(this);
and in ExternalDB (Is SqliteOpenHelper):
ExternalDB(Context context){
super(context, DB_Name, null, DATABASE_VERSION);
mycontext = context;
AssetDatabaseOpenHelper adb = new AssetDatabaseOpenHelper(context,DB_Name);
db = adb.OpenDatabase();
}
And AssetDatabaseOpenHelper:
public class AssetDatabaseOpenHelper {
private static String DB_NAME;
private Context context;
AssetDatabaseOpenHelper(Context context, String DB_NAME){
this.context = context;
AssetDatabaseOpenHelper.DB_NAME = DB_NAME;
}
public SQLiteDatabase OpenDatabase(){
File dbFile = context.getDatabasePath(DB_NAME);
if(!dbFile.exists()){
try{
CopyDatabase(dbFile);
}
catch (IOException e){
throw new RuntimeException("Error Creating source database", e);
}
}
// copyDataBase();
return SQLiteDatabase.openDatabase(dbFile.getPath(),null,SQLiteDatabase.OPEN_READWRITE);
}
private void CopyDatabase(File dbFile) throws IOException{
InputStream is = context.getAssets().open(DB_NAME);
OutputStream os = new FileOutputStream(dbFile);
byte[] buffer = new byte[1024];
while(is.read(buffer)>0){
os.write(buffer);
}
os.flush();
os.close();
is.close();
}
}
As I Mentioned i get this error on this line:
OutputStream os = new FileOutputStream(dbFile);
CopyDatabase get's called when dbFile does not exist. Right? And then you tell the FileOutputStream to open the dbFile which we have established does not exist. Thus, no such file or directory Error. Seems legit, doesn't it?
The error is arising because the folder "databases" does not exist so the database cannot be copied
Try:
public SQLiteDatabase OpenDatabase() {
File dbFile = context.getDatabasePath(DB_NAME);
if (!dbFile.exists()) {
try {
//check if "databases" folder exists and create it if needed
File destDir = context.getDatabasePath(DB_NAME).getParentFile();
if(!destDir.exists()){
destDir.mkdirs();
}
CopyDatabase(dbFile);
} catch (IOException e) {
throw new RuntimeException("Error Creating source database", e);
}
} // copyDataBase();
return SQLiteDatabase.openDatabase(dbFile.getPath(), null, SQLiteDatabase.OPEN_READWRITE);
}
For Any One having same problem, Hard Coding database paths didn't work for me and finaly writing copy function like this solved my problem:
/**
* Copy database file from assets folder inside the apk to the system database path.
* #param context Context
* #param databaseName Database file name inside assets folder
* #param overwrite True to rewrite on the database if exists
* #return True if the database have copied successfully or if the database already exists without overwrite, false otherwise.
*/
private boolean copyDatabaseFromAssets(Context context, String databaseName , boolean overwrite) {
File outputFile = context.getDatabasePath(databaseName);
if (outputFile.exists() && !overwrite) {
return true;
}
outputFile = context.getDatabasePath(databaseName + ".temp");
outputFile.getParentFile().mkdirs();
try {
InputStream inputStream = context.getAssets().open(databaseName);
OutputStream outputStream = new FileOutputStream(outputFile);
// transfer bytes from the input stream into the output stream
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
// Close the streams
outputStream.flush();
outputStream.close();
inputStream.close();
outputFile.renameTo(context.getDatabasePath(databaseName));
} catch (IOException e) {
if (outputFile.exists()) {
outputFile.delete();
}
return false;
}
return true;
}
I had to create the folder databases first and then try to create database file.
from this answer: https://stackoverflow.com/a/29058717/4225644
Related
I'm trying to use a prepopulated database in my Android project. My pipeline is this:
I'm creating a new database and processing a large JSON file to populate it (on Android). It takes a lot of time (~15 minutes or so), so I'd like to pack this created database and distribute it with the app.
I upload the database to Firebase Storage and download it manually on my PC. This database file, when checked with a browser from https://sqlitebrowser.org/, appears correct.
I add the downloaded database to Assets folder, and copy it over to original database path.
The database should work at this point, but it doesn't. There's no error message. It appears empty, but the copied file has proper size and everything. Restarting the app doesn't help, and calling new Room.databaseBuilders doesn't help either.
Code:
Database:
#Database(entities = {...}, version = 1)
public abstract class MyDatabase extends RoomDatabase {
public abstract DbDao dbDao();
private static MyDatabase instance = null;
public static MyDatabase getInstance(Context context) {
if (instance == null){
instance =
Room.databaseBuilder(context, MyDatabase.class, "db")
.build();
}
return instance;
}
Uploading:
public void uploadDB(){
String DBPath = mContext.getDatabasePath("db").getAbsolutePath();
File file = new File(DBPath);
StorageReference storageRef = FirebaseStorage.getInstance().getReference().child("db/my_database.db");
BufferedInputStream bis;
try {
bis = new BufferedInputStream(new FileInputStream(file));
} catch (FileNotFoundException e){
e.printStackTrace();
return;
}
storageRef.putStream(bis);
}
Copying:
public void loadDbFromAssets() throws IOException {
InputStream in = mContext.getAssets().open("databases/my_database.db");
String db_path = mContext.getDatabasePath("db").getAbsolutePath();
File out_file = new File(db_path);
if (out_file.exists()){
boolean deleted = out_file.delete();
if (!deleted) {
DebugLog.log("Old DB not deleted!");
return;
}
}
OutputStream out = new FileOutputStream(out_file);
copy(in, out);
File in_file = new File(db_path);
DebugLog.log("Copied file size: " + in_file.length() + "b");
}
public static void copy(InputStream in, OutputStream out) throws IOException{
try {
try {
byte[] buff = new byte[1024];
int len;
while ((len = in.read(buff)) > 0){
out.write(buff, 0, len);
}
} finally {
out.flush();
out.close();
}
} finally {
in.close();
}
}
Am I missing something?
EDIT: Solved using https://github.com/humazed/RoomAsset.
I have used pre-existing database in my application which placed in assets folder and I am using that pre-existing structured for storing data. this is working fine in all the Android devices.
But Somehow it is not working in Android P beta version. When I unzip the database and store into the internal memory and when closed that database object, database file has being corrupted so, after closed the database only "android-metadata" table is left in that database file other tables has been removed automatically.
Please advice.!! what will be the cause. Here is my code
public void open() throws SQLException {
try {
boolean isExist = mDatabaseHelper.checkDataBase();
if (isExist == false) {
mDatabase = mDatabaseHelper.getWritableDatabase();
mDatabaseHelper.copyFromZipFile();
if (mDatabase.isOpen()) {
mDatabase.close();
}
}
mDatabase = mDatabaseHelper.getWritableDatabase();
} catch (Exception e) {
Logger.d(TAG, e.getMessage());
e.printStackTrace();
}
}
/**
* This method is used to close the dataHelper object.
*/
public void close() {
try {
if (mDatabase != null && mDatabase.isOpen())
mDatabase.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean checkDataBase() {
PACKAGE = mContext.getApplicationContext().getPackageName();
DB_PATH = DATA + PACKAGE + "/databases/";
Logger.d(TAG, DB_PATH);
File f = new File(DB_PATH + mDatabaseName);
return f.exists();
}
public void copyFromZipFile() throws IOException {
InputStream is = mContext.getAssets().open("xyz.zip");
// Path to the just created empty db
PACKAGE = mContext.getApplicationContext().getPackageName();
Logger.d(TAG, DB_PATH);
File outFile = new File(DB_PATH, mDatabaseName);
//Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(outFile.getAbsolutePath());
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
try {
while (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);
}
baos.writeTo(myOutput);
}
} finally {
zis.close();
myOutput.flush();
myOutput.close();
is.close();
}
}
I am trying to copy a database named "adinpect" from the asset folder to the application databases folder, but it is not working...
Code (in main activity onCreate(), just for testing):
try {
String destPath = "/data/data/" + getPackageName() + "/databases";
File f = new File(destPath);
if (!f.exists()) {
f.mkdirs();
f.createNewFile();
//---copy the db from the assets folder into the databases folder---
CopyDB(getBaseContext().getAssets().open("adinspect"), new FileOutputStream(destPath + "/adinspect"));
}
}
catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
public void CopyDB(InputStream inputStream, OutputStream outputStream) throws IOException {
//---copy 1K bytes at a time---
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
inputStream.close();
outputStream.close();
}//end copyDB
The "databases" folder is created but there in nothing in it, trying to access it by DDMS.
I do not get any errors.
Any suggestion?
Thanks
This code wlll help you to copy DB from assets folder.
You can check first if DB exists or not.
try{
// CHECK IS EXISTS OR NOT
SQLiteDatabase dbe = SQLiteDatabase.openDatabase("/data/data/"+getPackageName+"/databases/dbname.sqlite",null, 0);
dbe.close();
}
catch(Exception e)}
{
// COPY IF NOT EXISTS
AssetManager am = getApplicationContext().getAssets();
OutputStream os = new FileOutputStream("/data/data/"+getPackageName+"/databases/dbname.sqlite");
byte[] b = new byte[100];
int r;
InputStream is = am.open("dbname.sqlite");
while ((r = is.read(b)) != -1) {
os.write(b, 0, r);
}
is.close();
os.close();
}
I am trying to copy DB from assets folder to device. This code is working fine on Emulator and rooted Device. I just want to know is it create any problem on unrooted device or it will work same.
private void StoreDatabase() {
File DbFile = new File(
"data/data/packagename/DBname.sqlite");
if (DbFile.exists()) {
System.out.println("file already exist ,No need to Create");
} else {
try {
DbFile.createNewFile();
System.out.println("File Created successfully");
InputStream is = this.getAssets().open("DBname.sqlite");
FileOutputStream fos = new FileOutputStream(DbFile);
byte[] buffer = new byte[1024];
int length = 0;
while ((length = is.read(buffer)) > 0) {
fos.write(buffer, 0, length);
}
System.out.println("File succesfully placed on sdcard");
// Close the streams
fos.flush();
fos.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
This will work for sure in all devices and emulator, no need to root.
/**
* Copies your database from your local assets-folder to the just created
* empty database in the system folder, from where it can be accessed and
* handled. This is done by transfering bytestream.
* */
private void copyDataBase(String dbname) throws IOException {
// Open your local db as the input stream
InputStream myInput = myContext.getAssets().open(dbname);
// Path to the just created empty db
File outFileName = myContext.getDatabasePath(dbname);
// 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();
}
/**
* Copy database file from assets folder inside the apk to the system database path.
* #param context Context
* #param databaseName Database file name inside assets folder
* #param overwrite True to rewrite on the database if exists
* #return True if the database have copied successfully or if the database already exists without overwrite, false otherwise.
*/
private boolean copyDatabaseFromAssets(Context context, String databaseName , boolean overwrite) {
File outputFile = context.getDatabasePath(databaseName);
if (outputFile.exists() && !overwrite) {
return true;
}
outputFile = context.getDatabasePath(databaseName + ".temp");
outputFile.getParentFile().mkdirs();
try {
InputStream inputStream = context.getAssets().open(databaseName);
OutputStream outputStream = new FileOutputStream(outputFile);
// transfer bytes from the input stream into the output stream
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
// Close the streams
outputStream.flush();
outputStream.close();
inputStream.close();
outputFile.renameTo(context.getDatabasePath(databaseName));
} catch (IOException e) {
if (outputFile.exists()) {
outputFile.delete();
}
return false;
}
return true;
}
I am not sure, but this works on every device I have tested on. I stole this method (from somewhere here) and made it generic for both backing up and restoring:
public static void movedb(File srcdb, File destdb)
{
try
{
if (Environment.getExternalStorageDirectory().canWrite())
{
if (srcdb.exists())
{
FileChannel src = new FileInputStream(srcdb).getChannel();
FileChannel dst = new FileOutputStream(destdb).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
}
else
{
//ERROR: "Database file references are incorrect"
}
}
else
{
//ERROR: "Cannot write to file"
}
}
catch (Exception e)
{
//ERROR: e.getMessage()
}
}
Then I just back it up by calling:
movedb(this, getDatabasePath(getDbName()), new File(Environment.getExternalStorageDirectory(), getDatabaseBackupPath()));
Where getDatabasePath() and getDatabaseBackupPath() are just string values
private void copyDataBase(Context context) throws IOException {
//Log.i(TAG, "Opening Asset...");
// Open your local db as the input stream
InputStream myInput = context.getAssets().open(DBHelper.DATABASE_NAME);
// Log.i(TAG, "Getting db path...");
// Path to the just created empty db
File dbFile = getDatabasePath(DBHelper.DATABASE_NAME);
if (!dbFile.exists()) {
SQLiteDatabase checkDB = context.openOrCreateDatabase(DBHelper.DATABASE_NAME, context.MODE_PRIVATE, null);
if (checkDB != null) {
checkDB.close();
}
}
//Log.i(TAG, "Getting output stream...");
// Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(dbFile);
// Log.i(TAG, "Writing data...");
// 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();
}
This works for Kotlin.
assets.open("sqlite_db_in_assets.db")
.copyTo(getDatabasePath("sqlite_db_in_device.db").outputStream())
While technically feasible, I don't believe copying (either to or from) potentially live database file is a good idea.
I used this tutorial to include a database file to my android app. It works fine on my HTC Decire HD. I wanted to run it on emulator to see if tablet layouts look well. Unfortunately the app fails with an error.
private void copyDataBase() throws IOException{
//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){ <------ HERE, at first iteration
myOutput.write(buffer, 0, length);
}
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
}
The message for this error is just 'null', nothing more. Can this be fixed?
private void copyfromAsset()
{
try {
String FILE_TO_READ="data.txt"; //file in asset folder
String TEMP_FILE_NAME="temp.txt"; //or whatever file name you want to give
byte[] buffer = new byte[1024];
int len1 = 0;
InputStream istr=(con.getAssets().open(FILE_TO_READ));
FileOutputStream fos=openFileOutput(TEMP_FILE_NAME,MODE_WORLD_READABLE);
while ((len1 = istr.read(buffer)) !=-1) {
fos.write(buffer, 0, len1); // Write In FileOutputStream.
}
fos.flush();
fos.close();
istr.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
try this method it is working fine for me....hit accept if you found usefull..
public void createDataBase() throws IOException {
boolean dbExist = checkDataBase();
if (dbExist) {
// do nothing - database already exist
}
else {
// By calling this method and empty database will be created into
// the default system path
// of your application so we are going to be able to overwrite that
// database with our database.
try {
copyDataBase();
}
catch (IOException e) {
throw new Error("Error copying database");
}
}
}
/**
* Check if the database already exist to avoid re-copying the file each
* time you open the application.
*
* #return true if it exists, false if it doesn't
*/
private boolean checkDataBase() {
File dbFile = new File(DB_PATH + DB_NAME);
return dbFile.exists();
}
/**
* Copies your database from your local assets-folder to the just created
* empty database in the
* system folder, from where it can be accessed and handled.
* This is done by transferring byte-stream.
*/
private void copyDataBase() throws IOException {
// Open your local DB as the input stream
InputStream myInput = mContext.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 input-file to the output-file
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();
}