Android 上 SQLite 数据库的简单导出和导入

Posted

技术标签:

【中文标题】Android 上 SQLite 数据库的简单导出和导入【英文标题】:Simple export and import of a SQLite database on Android 【发布时间】:2011-09-26 07:43:48 【问题描述】:

我正在尝试实现一个简单的SQLite 导出/导入以进行备份。导出只是存储原始current.db 文件的副本。我想要导入的只是删除旧的current.db 文件并将imported.db 文件重命名为current.db。这可能吗?当我尝试此解决方案时,我收到以下错误:

06-30 13:33:38.831: ERROR/SQLiteOpenHelper(23570):
    android.database.sqlite.SQLiteDatabaseCorruptException: error code 11: database disk image is malformed

如果我在 SQLite 浏览器中查看原始数据库文件,它看起来很好。

【问题讨论】:

【参考方案1】:

我在我的一个应用程序的SQLiteOpenHelper 中使用此代码来导入数据库文件。

编辑:我将FileUtils.copyFile() 方法粘贴到问题中。

SQLiteOpenHelper

public static String DB_FILEPATH = "/data/data/package_name/databases/database.db";

/**
 * Copies the database file at the specified location over the current
 * internal application database.
 * */
public boolean importDatabase(String dbPath) throws IOException 

    // Close the SQLiteOpenHelper so it will commit the created empty
    // database to internal storage.
    close();
    File newDb = new File(dbPath);
    File oldDb = new File(DB_FILEPATH);
    if (newDb.exists()) 
        FileUtils.copyFile(new FileInputStream(newDb), new FileOutputStream(oldDb));
        // Access the copied database so SQLiteHelper will cache it and mark
        // it as created.
        getWritableDatabase().close();
        return true;
    
    return false;

FileUtils

public class FileUtils 
    /**
     * Creates the specified <code>toFile</code> as a byte for byte copy of the
     * <code>fromFile</code>. If <code>toFile</code> already exists, then it
     * will be replaced with a copy of <code>fromFile</code>. The name and path
     * of <code>toFile</code> will be that of <code>toFile</code>.<br/>
     * <br/>
     * <i> Note: <code>fromFile</code> and <code>toFile</code> will be closed by
     * this function.</i>
     * 
     * @param fromFile
     *            - FileInputStream for the file to copy from.
     * @param toFile
     *            - FileInputStream for the file to copy to.
     */
    public static void copyFile(FileInputStream fromFile, FileOutputStream toFile) throws IOException 
        FileChannel fromChannel = null;
        FileChannel toChannel = null;
        try 
            fromChannel = fromFile.getChannel();
            toChannel = toFile.getChannel();
            fromChannel.transferTo(0, fromChannel.size(), toChannel);
         finally 
            try 
                if (fromChannel != null) 
                    fromChannel.close();
                
             finally 
                if (toChannel != null) 
                    toChannel.close();
                
            
        
    

如有必要,不要忘记删除旧的数据库文件。

【讨论】:

记得给你喜欢的答案投票,帮助社区知道哪些是好的,哪些是垃圾。 Austyn 的上述答案也对我有用。有一点需要注意,这里没有说明,使用 String path = Environment.getExternalStorageDirectory().toString() + "/appName/";设置 SD 卡上的写入路径。 这是有道理的。这是一些很好的建议。我实际上是在自己的应用程序中执行此操作,但在此演示代码中仅包含一个字符串文字作为占位符。 @AustynMahoney:这可以在非 root 设备上使用吗? 数据库路径:context.getDatabasePath("db_filename")【参考方案2】:

这是将数据库导出到名为备份文件夹的文件夹的简单方法,您可以根据需要对其进行命名,以及从同一文件夹中导入数据库的简单方法a

    public class ExportImportDB extends Activity 
        @Override
        protected void onCreate(Bundle savedInstanceState) 
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
//creating a new folder for the database to be backuped to
            File direct = new File(Environment.getExternalStorageDirectory() + "/Exam Creator");

               if(!direct.exists())
                
                    if(direct.mkdir()) 
                      
                       //directory is created;
                      

                
            exportDB();
            importDB();

        
    //importing database
        private void importDB() 
            // TODO Auto-generated method stub

            try 
                File sd = Environment.getExternalStorageDirectory();
                File data  = Environment.getDataDirectory();

                if (sd.canWrite()) 
                    String  currentDBPath= "//data//" + "PackageName"
                            + "//databases//" + "DatabaseName";
                    String backupDBPath  = "/BackupFolder/DatabaseName";
                    File  backupDB= new File(data, currentDBPath);
                    File currentDB  = new File(sd, backupDBPath);

                    FileChannel src = new FileInputStream(currentDB).getChannel();
                    FileChannel dst = new FileOutputStream(backupDB).getChannel();
                    dst.transferFrom(src, 0, src.size());
                    src.close();
                    dst.close();
                    Toast.makeText(getBaseContext(), backupDB.toString(),
                            Toast.LENGTH_LONG).show();

                
             catch (Exception e) 

                Toast.makeText(getBaseContext(), e.toString(), Toast.LENGTH_LONG)
                        .show();

            
        
    //exporting database 
        private void exportDB() 
            // TODO Auto-generated method stub

            try 
                File sd = Environment.getExternalStorageDirectory();
                File data = Environment.getDataDirectory();

                if (sd.canWrite()) 
                    String  currentDBPath= "//data//" + "PackageName"
                            + "//databases//" + "DatabaseName";
                    String backupDBPath  = "/BackupFolder/DatabaseName";
                    File currentDB = new File(data, currentDBPath);
                    File backupDB = new File(sd, backupDBPath);

                    FileChannel src = new FileInputStream(currentDB).getChannel();
                    FileChannel dst = new FileOutputStream(backupDB).getChannel();
                    dst.transferFrom(src, 0, src.size());
                    src.close();
                    dst.close();
                    Toast.makeText(getBaseContext(), backupDB.toString(),
                            Toast.LENGTH_LONG).show();

                
             catch (Exception e) 

                Toast.makeText(getBaseContext(), e.toString(), Toast.LENGTH_LONG)
                        .show();

            
        

    

不要忘记添加此权限以继续它

  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
    </uses-permission>

享受

【讨论】:

importDB 和 exportDB 过程不知何故完全相同? 错误:如果仔细观察,'currentDB' 变量指的是 importDB 中的 data 文件夹文件,以及 exportDB 中的 sd 文件夹。 还有一个问题,你如何通过点击android上的一个按钮来实现它。 如果在复制过程中数据库文件发生变化会怎样? 小心实现。在数据库导出期间的任何错误情况下都会出现内存泄漏。原因是两个流(FileInputStream 和 FileOutputStream)都没有在 finally 中关闭。【参考方案3】:

导出 db 而不是 SQLITE 或 ROOM:

首先,在 AndroidManifest.xml 文件中添加这个权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

其次,我们开始编写 db 函数:

private void exportDB() 
    try 
        File dbFile = new File(this.getDatabasePath(DATABASE_NAME).getAbsolutePath());
        FileInputStream fis = new FileInputStream(dbFile);

        String outFileName = DirectoryName + File.separator +
                DATABASE_NAME + ".db";

        // Open the empty db as the output stream
        OutputStream output = new FileOutputStream(outFileName);

        // Transfer bytes from the inputfile to the outputfile
        byte[] buffer = new byte[1024];
        int length;
        while ((length = fis.read(buffer)) > 0) 
            output.write(buffer, 0, length);
        
        // Close the streams
        output.flush();
        output.close();
        fis.close();


     catch (IOException e) 
        Log.e("dbBackup:", e.getMessage());
    

每天创建文件夹,文件夹名称为当前日期:

public void createBackup() 

    sharedPref = getSharedPreferences("dbBackUp", MODE_PRIVATE);
    editor = sharedPref.edit();

    String dt = sharedPref.getString("dt", new SimpleDateFormat("dd-MM-yy").format(new Date()));

    if (dt != new SimpleDateFormat("dd-MM-yy").format(new Date())) 
        editor.putString("dt", new SimpleDateFormat("dd-MM-yy").format(new Date()));

        editor.commit();
    

    File folder = new File(Environment.getExternalStorageDirectory() + File.separator + "BackupDBs");
    boolean success = true;
    if (!folder.exists()) 
        success = folder.mkdirs();
    
    if (success) 

        DirectoryName = folder.getPath() + File.separator + sharedPref.getString("dt", "");
        folder = new File(DirectoryName);
        if (!folder.exists()) 
            success = folder.mkdirs();
        
        if (success) 
            exportDB();
        
     else 
        Toast.makeText(this, "Not create folder", Toast.LENGTH_SHORT).show();
    



分配不带 .db 扩展名的 DATABASE_NAME,其数据类型为字符串

【讨论】:

【参考方案4】:

如果你想在 kotlin 中使用它。并且完美运行

 private fun exportDbFile() 

    try 

        //Existing DB Path
        val DB_PATH = "/data/packagename/databases/mydb.db"
        val DATA_DIRECTORY = Environment.getDataDirectory()
        val INITIAL_DB_PATH = File(DATA_DIRECTORY, DB_PATH)

        //COPY DB PATH
        val EXTERNAL_DIRECTORY: File = Environment.getExternalStorageDirectory()
        val COPY_DB = "/mynewfolder/mydb.db"
        val COPY_DB_PATH = File(EXTERNAL_DIRECTORY, COPY_DB)

        File(COPY_DB_PATH.parent!!).mkdirs()
        val srcChannel = FileInputStream(INITIAL_DB_PATH).channel

        val dstChannel = FileOutputStream(COPY_DB_PATH).channel
        dstChannel.transferFrom(srcChannel,0,srcChannel.size())
        srcChannel.close()
        dstChannel.close()

     catch (excep: Exception) 
        Toast.makeText(this,"ERROR IN COPY $excep",Toast.LENGTH_LONG).show()
        Log.e("FILECOPYERROR>>>>",excep.toString())
        excep.printStackTrace()
    


【讨论】:

它给我一个错误,找不到这样的文件或目录【参考方案5】:

在 Android 上导入和导出 SQLite 数据库

这是我将数据库导出到设备存储的功能

private void exportDB()
    String DatabaseName = "Sycrypter.db";
    File sd = Environment.getExternalStorageDirectory();
    File data = Environment.getDataDirectory();
    FileChannel source=null;
    FileChannel destination=null;
    String currentDBPath = "/data/"+ "com.synnlabz.sycryptr" +"/databases/"+DatabaseName ;
    String backupDBPath = SAMPLE_DB_NAME;
    File currentDB = new File(data, currentDBPath);
    File backupDB = new File(sd, backupDBPath);
    try 
        source = new FileInputStream(currentDB).getChannel();
        destination = new FileOutputStream(backupDB).getChannel();
        destination.transferFrom(source, 0, source.size());
        source.close();
        destination.close();
        Toast.makeText(this, "Your Database is Exported !!", Toast.LENGTH_LONG).show();
     catch(IOException e) 
        e.printStackTrace();
    

这是我将数据库从设备存储导入到 android 应用程序的功能

private void importDB()
    String dir=Environment.getExternalStorageDirectory().getAbsolutePath();
    File sd = new File(dir);
    File data = Environment.getDataDirectory();
    FileChannel source = null;
    FileChannel destination = null;
    String backupDBPath = "/data/com.synnlabz.sycryptr/databases/Sycrypter.db";
    String currentDBPath = "Sycrypter.db";
    File currentDB = new File(sd, currentDBPath);
    File backupDB = new File(data, backupDBPath);

    try 
        source = new FileInputStream(currentDB).getChannel();
        destination = new FileOutputStream(backupDB).getChannel();
        destination.transferFrom(source, 0, source.size());
        source.close();
        destination.close();
        Toast.makeText(this, "Your Database is Imported !!", Toast.LENGTH_SHORT).show();
     catch (IOException e) 
        e.printStackTrace();
    

【讨论】:

以上是关于Android 上 SQLite 数据库的简单导出和导入的主要内容,如果未能解决你的问题,请参考以下文章

SQLite----Android Studio3.6.3 当前最新版本数据库查找与导出方法

在 Android Studio 上调试数据库 ( SQLite ) (转)

android sqlite数据库操作

Android SQLite数据库加密

Android SQLite数据库加密

Android SQLite数据库加密