Reputation: 5708
I have the following code:
private void copyDataBase() throws IOException {
Context context = this.getApplicationContext();
// Open your local db as the input stream
InputStream myInput = context.getAssets().open(DB_NAME);
// Define output file name
File outfile = new File(context.getDatabasePath(DB_NAME).toString());
outfile.setWritable(true);
// Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(outfile);
// 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();
}
However, it keeps failing on the OutputStream
variable. The error that keeps getting thrown is:
java.io.FileNotFoundException ... mydb.db: open failed: ENOTDIR (Not a directory)
My SQLite file is in my assets directory in the project, and my minSdkVersion
is 19
and targetSdkVersion
is 28
.
This is an updated version of the function call from comments below, and it is still failing.
private void copyDataBase() throws IOException {
Context context = this.getApplicationContext();
// Open your local db as the input stream
InputStream myInput = context.getAssets().open(DB_NAME);
// Define output file name
File outfile = new File(context.getDatabasePath(DB_NAME).toString());
try {
// Define path to database file and make directories if necessary
File dir = new File(DB_PATH);
if (!dir.exists()) {
boolean madeDirectory = dir.mkdirs();
Log.d(TAG, String.format("copyDataBase: Made directories: %s", madeDirectory));
}
if (!outfile.exists()){
outfile.createNewFile();
}
} catch (Exception e) {
e.printStackTrace();
}
// Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(outfile);
// 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();
}
The variable DB_NAME
is merely the name of the database file I am using, myExample.db
The string generated from the getDatabasePath function call is "/data/data/projectname/databases/myExample.db
Upvotes: 2
Views: 1097
Reputation: 56948
I believe that your issue is that the databases folder doesn't exist. Rather just /data/data/<the_package>/
exists.
So try adding the code as per the comments :-
File outfile = new File(context.getDatabasePath(DB_NAME).toString()); //<<<<<<<<< EXISTING LINE
if (!outfile.getParentFile().exists()) { //<<<<<<<<<< ADDED
outfile.getParentFile().mkdirs(); //<<<<<<<<<< ADDED
} //<<<<<<<<<< ADDED
I tend do the directory creation in when checking to see if the database exists e.g. :-
private boolean checkDataBase() {
/**
* Does not open the database instead checks to see if the file exists
* also creates the databases directory if it does not exist
* (the real reason why the database is opened, which appears to result in issues)
*/
File db = new File(myContext.getDatabasePath(DB_NAME).getPath()); //Get the file name of the database
if (db.exists()) return true; // If it exists then return doing nothing
// Get the parent (directory in which the database file would be)
File dbdir = db.getParentFile();
// If the directory does not exist then make the directory (and higher level directories if need be)
if (!dbdir.exists()) {
dbdir.mkdirs();
}
return false;
}
Using the code :-
private void copyDataBase() throws IOException {
final String TAG = "COPYDATABASE";
//Open your local db as the input stream
Log.d(TAG,"Initiated Copy of the database file " + DB_NAME + " from the assets folder.");
InputStream myInput = myContext.getAssets().open(DB_NAME); // Open the Asset file
String dbpath = myContext.getDatabasePath(DB_NAME).getPath();
Log.d(TAG,"Asset file " + DB_NAME + " found so attmepting to copy to " + dbpath);
// Path to the just created empty db
//String outFileName = DB_PATH + DB_NAME;
//Open the empty db as the output stream
File outfile = new File(myContext.getDatabasePath(DB_NAME).toString());
Log.d("DBPATH","path is " + outfile.getPath());
outfile.setWritable(true);
//OutputStream myoutputx2 = new FileOutputStream(outfile);
if (!outfile.getParentFile().exists()) {
outfile.getParentFile().mkdirs();
}
OutputStream myOutput = new FileOutputStream(outfile);
//transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[buffer_size];
int length;
while ((length = myInput.read(buffer))>0) {
blocks_copied++;
Log.d(TAG,"Ateempting copy of block " + String.valueOf(blocks_copied) + " which has " + String.valueOf(length) + " bytes.");
myOutput.write(buffer, 0, length);
bytes_copied += length;
}
Log.d(TAG,
"Finished copying Database " + DB_NAME +
" from the assets folder, to " + dbpath +
String.valueOf(bytes_copied) + "were copied, in " +
String.valueOf(blocks_copied) + " blocks of size " +
String.valueOf(buffer_size) + "."
);
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
Log.d(TAG,"All Streams have been flushed and closed.");
}
Results in the error you are getting as per :-
02-21 10:17:25.975 7604-7604/so.cdfa D/VERSION: Build Version is 22
02-21 10:17:25.975 7604-7604/so.cdfa D/DBPATH: DB Path is /data/data/so.cdfa/databases/db
02-21 10:17:25.975 7604-7604/so.cdfa D/DBPATH: DB Path is /data/data/so.cdfa/databases/db
02-21 10:17:25.976 7604-7604/so.cdfa D/COPYDATABASE: Initiated Copy of the database file db from the assets folder.
02-21 10:17:25.976 7604-7604/so.cdfa D/COPYDATABASE: Asset file db found so attmepting to copy to /data/data/so.cdfa/databases/db
02-21 10:17:25.976 7604-7604/so.cdfa D/DBPATH: path is /data/data/so.cdfa/databases/db
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: java.io.FileNotFoundException: /data/data/so.cdfa/databases/db: open failed: ENOENT (No such file or directory)
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: at libcore.io.IoBridge.open(IoBridge.java:456)
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: at java.io.FileOutputStream.<init>(FileOutputStream.java:87)
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: at java.io.FileOutputStream.<init>(FileOutputStream.java:72)
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: at so.cdfa.DBHelper.copyDataBase(DBHelper.java:120)
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: at so.cdfa.DBHelper.createDataBase(DBHelper.java:58)
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: at so.cdfa.DBHelper.<init>(DBHelper.java:32)
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: at so.cdfa.MainActivity.onCreate(MainActivity.java:60)
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: at android.app.Activity.performCreate(Activity.java:5990)
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
02-21 10:17:25.976 7604-7604/so.cdfa W/System.err: at android.app.ActivityThread.access$800(ActivityThread.java:151)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: at android.os.Looper.loop(Looper.java:135)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5254)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: at java.lang.reflect.Method.invoke(Native Method)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: at java.lang.reflect.Method.invoke(Method.java:372)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: at libcore.io.Posix.open(Native Method)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: at libcore.io.IoBridge.open(IoBridge.java:442)
02-21 10:17:25.977 7604-7604/so.cdfa W/System.err: ... 19 more
02-21 10:17:25.977 7604-7604/so.cdfa D/AndroidRuntime: Shutting down VM
02-21 10:17:25.977 7604-7604/so.cdfa E/AndroidRuntime: FATAL EXCEPTION: main
Process: so.cdfa, PID: 7604
java.lang.RuntimeException: Unable to start activity ComponentInfo{so.cdfa/so.cdfa.MainActivity}: java.lang.RuntimeException: Error copying database (see stack-trace above)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Caused by: java.lang.RuntimeException: Error copying database (see stack-trace above)
at so.cdfa.DBHelper.createDataBase(DBHelper.java:65)
Amending the code to (as per the original answer):-
private void copyDataBase() throws IOException {
final String TAG = "COPYDATABASE";
//Open your local db as the input stream
Log.d(TAG,"Initiated Copy of the database file " + DB_NAME + " from the assets folder.");
InputStream myInput = myContext.getAssets().open(DB_NAME); // Open the Asset file
String dbpath = myContext.getDatabasePath(DB_NAME).getPath();
Log.d(TAG,"Asset file " + DB_NAME + " found so attmepting to copy to " + dbpath);
// Path to the just created empty db
//String outFileName = DB_PATH + DB_NAME;
//Open the empty db as the output stream
File outfile = new File(myContext.getDatabasePath(DB_NAME).toString());
Log.d("DBPATH","path is " + outfile.getPath());
outfile.setWritable(true); //<<<<<<<<<< not required as writable access exists
//OutputStream myoutputx2 = new FileOutputStream(outfile);
if (!outfile.getParentFile().exists()) {
outfile.getParentFile().mkdirs();
}
OutputStream myOutput = new FileOutputStream(outfile);
//transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[buffer_size];
int length;
while ((length = myInput.read(buffer))>0) {
blocks_copied++;
Log.d(TAG,"Ateempting copy of block " + String.valueOf(blocks_copied) + " which has " + String.valueOf(length) + " bytes.");
myOutput.write(buffer, 0, length);
bytes_copied += length;
}
Log.d(TAG,
"Finished copying Database " + DB_NAME +
" from the assets folder, to " + dbpath +
String.valueOf(bytes_copied) + "were copied, in " +
String.valueOf(blocks_copied) + " blocks of size " +
String.valueOf(buffer_size) + "."
);
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
Log.d(TAG,"All Streams have been flushed and closed.");
}
Results in :-
02-21 10:24:31.808 7972-7972/? D/VERSION: Build Version is 22
02-21 10:24:31.808 7972-7972/? D/DBPATH: DB Path is /data/data/so.cdfa/databases/db
02-21 10:24:31.808 7972-7972/? D/DBPATH: DB Path is /data/data/so.cdfa/databases/db
02-21 10:24:31.808 7972-7972/? D/COPYDATABASE: Initiated Copy of the database file db from the assets folder.
02-21 10:24:31.808 7972-7972/? D/COPYDATABASE: Asset file db found so attmepting to copy to /data/data/so.cdfa/databases/db
02-21 10:24:31.808 7972-7972/? D/DBPATH: path is /data/data/so.cdfa/databases/db
02-21 10:24:31.809 7972-7972/? D/COPYDATABASE: Ateempting copy of block 1 which has 1024 bytes.
02-21 10:24:31.809 7972-7972/? D/COPYDATABASE: Ateempting copy of block 2 which has 1024 bytes.
02-21 10:24:31.809 7972-7972/? D/COPYDATABASE: Ateempting copy of block 3 which has 1024 bytes.
02-21 10:24:31.809 7972-7972/? D/COPYDATABASE: Ateempting copy of block 4 which has 1024 bytes.
02-21 10:24:31.809 7972-7972/? D/COPYDATABASE: Ateempting copy of block 5 which has 1024 bytes.
02-21 10:24:31.809 7972-7972/? D/COPYDATABASE: Ateempting copy of block 6 which has 1024 bytes.
02-21 10:24:31.809 7972-7972/? D/COPYDATABASE: Ateempting copy of block 7 which has 1024 bytes.
02-21 10:24:31.809 7972-7972/? D/COPYDATABASE: Ateempting copy of block 8 which has 1024 bytes.
02-21 10:24:31.809 7972-7972/? D/COPYDATABASE: Ateempting copy of block 9 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 10 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 11 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 12 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 13 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 14 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 15 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 16 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 17 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 18 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 19 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 20 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 21 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 22 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 23 which has 1024 bytes.
02-21 10:24:31.810 7972-7972/? D/COPYDATABASE: Ateempting copy of block 24 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 25 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 26 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 27 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 28 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 29 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 30 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 31 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 32 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 33 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 34 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 35 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 36 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 37 which has 1024 bytes.
02-21 10:24:31.811 7972-7972/? D/COPYDATABASE: Ateempting copy of block 38 which has 1024 bytes.
02-21 10:24:31.812 7972-7972/? D/COPYDATABASE: Ateempting copy of block 39 which has 1024 bytes.
02-21 10:24:31.812 7972-7972/? D/COPYDATABASE: Ateempting copy of block 40 which has 1024 bytes.
02-21 10:24:31.812 7972-7972/? D/COPYDATABASE: Finished copying Database db from the assets folder, to /data/data/so.cdfa/databases/db40960were copied, in 40 blocks of size 1024.
02-21 10:24:31.812 7972-7972/? D/COPYDATABASE: All Streams have been flushed and closed.
:-
public class DBHelper extends SQLiteOpenHelper {
private static String DB_NAME = "db";
private SQLiteDatabase myDataBase;
private final Context myContext;
private int bytes_copied = 0;
private static int buffer_size = 1024;
private int blocks_copied = 0;
public DBHelper(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
// Check for and create (copy DB from assets) when constructing the DBHelper
if (!checkDataBase()) {
bytes_copied = 0;
blocks_copied = 0;
createDataBase();
}
}
/**
* Creates an empty database on the system and rewrites it with your own database.
* */
public void createDataBase() {
boolean dbExist = checkDataBase(); // Double check
if(dbExist){
//do nothing - database already exist
} else {
//By calling this method an empty database will be created into the default system path
//of your application so we are gonna be able to overwrite that database with our database.
//this.getReadableDatabase();
//<<<<<<<<<< Dimsiss the above comment
//By calling this method an empty database IS NOT created nor are the related -shm and -wal files
//The method that creates the database is flawed and was only used to resolve the issue
//of the copy failing in the absence of the databases directory.
//The dbExist method, now utilised, checks for and creates the database directory, so there
//is then no need to create the database just to create the databases library. As a result
//the -shm and -wal files will not exist and thus result in the error associated with
//Android 9+ failing with due to tables not existining after an apparently successful
//copy.
try {
copyDataBase();
} catch (IOException e) {
File db = new File(myContext.getDatabasePath(DB_NAME).getPath());
if (db.exists()) {
db.delete();
}
e.printStackTrace();
throw new RuntimeException("Error copying database (see stack-trace above)");
}
}
}
/**
* 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() {
/**
* Does not open the database instead checks to see if the file exists
* also creates the databases directory if it does not exists
* (the real reason why the database is opened, which appears to result in issues)
*/
File db = new File(myContext.getDatabasePath(DB_NAME).getPath()); //Get the file name of the database
Log.d("DBPATH","DB Path is " + db.getPath());
if (db.exists()) return true; // If it exists then return doing nothing
// Get the parent (directory in which the database file would be)
File dbdir = db.getParentFile();
// If the directory does not exits then make the directory (and higher level directories)
/*
if (!dbdir.exists()) {
db.getParentFile().mkdirs();
dbdir.mkdirs();
}
*/
return false;
}
/**
* 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() throws IOException {
final String TAG = "COPYDATABASE";
//Open your local db as the input stream
Log.d(TAG,"Initiated Copy of the database file " + DB_NAME + " from the assets folder.");
InputStream myInput = myContext.getAssets().open(DB_NAME); // Open the Asset file
String dbpath = myContext.getDatabasePath(DB_NAME).getPath();
Log.d(TAG,"Asset file " + DB_NAME + " found so attmepting to copy to " + dbpath);
// Path to the just created empty db
//String outFileName = DB_PATH + DB_NAME;
//Open the empty db as the output stream
File outfile = new File(myContext.getDatabasePath(DB_NAME).toString());
Log.d("DBPATH","path is " + outfile.getPath());
//outfile.setWritable(true); // NOT NEEDED as permission already applies
//OutputStream myoutputx2 = new FileOutputStream(outfile);
/* Note done in checkDatabase method
if (!outfile.getParentFile().exists()) {
outfile.getParentFile().mkdirs();
}
*/
OutputStream myOutput = new FileOutputStream(outfile);
//transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[buffer_size];
int length;
while ((length = myInput.read(buffer))>0) {
blocks_copied++;
Log.d(TAG,"Ateempting copy of block " + String.valueOf(blocks_copied) + " which has " + String.valueOf(length) + " bytes.");
myOutput.write(buffer, 0, length);
bytes_copied += length;
}
Log.d(TAG,
"Finished copying Database " + DB_NAME +
" from the assets folder, to " + dbpath +
String.valueOf(bytes_copied) + "were copied, in " +
String.valueOf(blocks_copied) + " blocks of size " +
String.valueOf(buffer_size) + "."
);
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
Log.d(TAG,"All Streams have been flushed and closed.");
}
@Override
public synchronized void close() {
if(myDataBase != null)
myDataBase.close();
super.close();
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
@Override
public void onConfigure(SQLiteDatabase db) {
super.onConfigure(db);
Log.d("DBCONFIGURE","Database has been configured ");
}
@Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
Log.d("DBOPENED","Database has been opened.");
}
}
Upvotes: 2
Reputation: 24211
The error says it could not find the output file. Hence you need to create one first in order to open that and copy from the asset.
try {
String DB_PATH = "/data/data/" + "your.application.package.name" + "/databases/";
File dir = new File(DB_PATH);
if (!dir.exists()) dir.mkdir();
File f = new File(DB_PATH + DB_NAME);
if (!f.exists()) {
f.createNewFile();
}
} catch (Exception e) {
e.printStackTrace();
}
// Then it should work
OutputStream mOutput = new FileOutputStream(f);
Upvotes: 1