从无根设备中的资产文件夹复制数据库

Posted

技术标签:

【中文标题】从无根设备中的资产文件夹复制数据库【英文标题】:Copy Database from assets folder in unrooted device 【发布时间】:2012-05-31 02:29:45 【问题描述】:

我正在尝试将数据库从资产文件夹复制到设备。此代码在模拟器和根设备上运行良好。我只是想知道它是否会在无根设备上产生任何问题,或者它会起作用。

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();
        
    


【问题讨论】:

是的,您的代码 sn-p 也可以在无根设备上完美运行 :) 【参考方案1】:

这肯定适用于所有设备和模拟器,无需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();

【讨论】:

我的代码也可以正常工作我只是想知道它是否会在无根设备上产生任何问题? 从不使用硬编码路径outFileName 值在许多 android 环境(例如辅助帐户)上都是错误的。使用getDatabasePath() 导出数据库文件的路径。 感谢@CommonsWare。我会改成String outFileName = getDatabasePath() + dbname; 不应该是 myContext.getDatabasePath(dbname) 吗? getDatabasePath(dbName) 也返回一个文件对象而不是一个字符串。 这似乎会导致导出加密的数据库文件(请参阅问题 - ***.com/questions/44397083/…)【参考方案2】:
    /**
 * 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;

【讨论】:

我给了 +1 因为上下文对象的 getDatabasePath() 方法和代码的 outputFile.getParentFile().mkdirs() 的组合解决了我的问题,即当我复制数据库失败时使用其他示例显示的硬编码目录路径。【参考方案3】:

我不确定,但这适用于我测试过的每台设备。我偷了这个方法(从这里的某个地方),并将它用于备份和恢复:

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()
    

然后我只是通过调用来备份它:

movedb(this, getDatabasePath(getDbName()), new File(Environment.getExternalStorageDirectory(), getDatabaseBackupPath()));

其中getDatabasePath()getDatabaseBackupPath() 只是字符串值

【讨论】:

我的代码也可以正常工作我只是想知道它是否会在无根设备上产生任何问题?【参考方案4】:
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();

【讨论】:

【参考方案5】:

这适用于 Kotlin。

assets.open("sqlite_db_in_assets.db")
      .copyTo(getDatabasePath("sqlite_db_in_device.db").outputStream())

【讨论】:

【参考方案6】:

虽然技术上可行,但我认为复制(复制到或复制)潜在的实时数据库文件不是一个好主意。

【讨论】:

这应该是一条评论。为什么你认为它值得成为一个答案? @Dharman 因为如果正确,则有理由关闭整个问题,将其标记为“不好的做法”。不知道它在这里是如何工作的...... 这里的工作方式是,除非他们直接回答问题,否则我们会在问题下方发布诸如 cmets 之类的评论。如果这是一种不好的做法,您应该警告他们,但他们可能仍然希望问题得到回答。 你看,这是一个我正在寻找答案的实际问题。对我来说,这听起来像是一个糟糕的问题,答案也很糟糕。我不是版主,所以关闭的东西不取决于我,但这也不仅仅是我在这里发表的评论。但请随时投票。 啊哈 :) 一旦我把头绕在 wal_checkpoint 上,我会回来详细说明。如果我有时间的话。在那之前,我提供了 25 年的 sysadm 和编程经验直觉。欢迎读者接受或离开。

以上是关于从无根设备中的资产文件夹复制数据库的主要内容,如果未能解决你的问题,请参考以下文章

将 db 从资产复制到设备数据文件夹时出现 FileNotFoundException

以编程方式从应用程序的资产文件夹复制数据库文件

C#:在 Android 上复制资产

将目录从资产复制到数据文件夹

Android SQLite:W / System.err:java.io.FileNotFoundException无法从资产复制数据库

Angular cli从资产文件夹中复制文件