将 .pdf 文件存储到 SQLite 数据库 Kotlin

问题描述 投票:0回答:1

我想将 .pdf 文件存储到 SQLite 数据库中。我知道首选方法是将 .pdf 本地存储在 Android 设备上,然后将路径存储在数据库中,但我希望用户能够下载 apk,而不必担心进行任何其他设备设置。我还读到,为了将 .pdf 存储在数据库中,需要将其转换为字节,然后存储在 BLOB 中。然而,我是用 Kotlin 写的,实际上我不确定从哪里开始转换为字节然后存储在 BLOB 中。谁能提供示例代码来执行此操作?

sqlite kotlin android-studio pdf
1个回答
0
投票

我知道首选方法是将 .pdf 本地存储在 Android 设备上,然后将路径存储在数据库中,但我希望用户能够下载 apk,而不必担心进行任何其他设备设置。

APK 不仅限于数据库文件,各种文件都可以作为资产或资源包含在包中。

您在尝试检索存储在数据库中的 pdf 文件时很可能会遇到由于文件大小而导致的问题。

我还读到,为了将 .pdf 存储在数据库中,需要将其转换为字节,然后存储在 BLOB 中。

文件是字节流(下面的演示展示了这一点)。

因此,仍然建议您仅在数据库中存储一个值(例如文件名),然后从合适的位置检索 pdf 文件。


演示


首先有 2 个 PDF 存在于任何地方(仅在 PC 上):-

SQLite 工具(本例中为 Navicat for SQLite)用于使用以下 SQL 创建预填充数据库:-

DROP TABLE IF EXISTS pdf;
CREATE TABLE IF NOT EXISTS pdf (id INTEGER PRIMARY KEY, pdf_title TEXT, pdf_name TEXT);
INSERT INTO pdf VALUES (1,'Blah PDF','pdf001.pdf'),(2,'Not Blah PDF','pdf002.pdf');

导致:-

数据库关闭,连接关闭,Navitcat for SQLite 关闭。生成一个文件,也位于 PC 上:-

现阶段已有基础数据。

在 Android Studio 中创建一个新的空 Kotlin 项目,并创建 assets 文件夹。复制 3 个文件(2 个 PDF 和数据库)(数据库文件重命名为 mypdfstore.db):-

创建了一个扩展 SQLiteOpenHelper 类的类,名为 DBHelper:-

const val DATABASE_NAME = "mypdfstore.db"
const val DATABASE_VERSION = 1

const val PDF_TABLE_NAME = "pdf"
const val PDF_ID_COLUMN = "id"
const val PDF_TITLE_COLUMN = "pdf_title"
const val PDF_NAME_COLUMN = "pdf_name"
const val PDF_CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS $PDF_TABLE_NAME ($PDF_ID_COLUMN INTEGER PRIMARY KEY, $PDF_TITLE_COLUMN TEXT, $PDF_NAME_COLUMN TEXT);"

class DBHelper(context: Context): SQLiteOpenHelper(context, DATABASE_NAME,null, DATABASE_VERSION) {
    override fun onCreate(p0: SQLiteDatabase?) {
        if (p0 != null) p0.execSQL(PDF_CREATE_TABLE_SQL)
    }

    override fun onUpgrade(p0: SQLiteDatabase?, p1: Int, p2: Int) {
        TODO("Not yet implemented")
    }

    companion object {
        private var instance: DBHelper?=null
        fun getInstance(context: Context): DBHelper {
            if (instance==null) {
                if(!ifDBExists(DATABASE_NAME,context)) {
                    getDBFromAsset(DATABASE_NAME, DATABASE_NAME,context)
                }
                instance = DBHelper(context)
            }
            return instance as DBHelper
        }
        fun ifDBExists(databaseName: String, context: Context): Boolean {
            val db_file = context.getDatabasePath(databaseName)
            if (db_file.exists()) return true
            val db_directory = db_file.parentFile
            if (db_directory != null) {
                if (!db_directory.exists()) db_directory.mkdirs()
            }
            return false
        }
        fun getDBFromAsset(assetName: String, databaseName: String, context: Context) {
            val i = context.assets.open(assetName)
            val o = context.getDatabasePath(databaseName).outputStream()
            val bufferSize = 1024 * 8
            val buffer = ByteArray(bufferSize)
            while (i.read(buffer) > 0) {
                o.write(buffer)
            }
            o.flush()
            o.close()
            i.close()
        }
    }
    fun getAllFromPDF(): Cursor {
        return this.writableDatabase.query(PDF_TABLE_NAME,null,null,null,null,null,null)
    }
}
  • 伴生对象值得特别关注,它
    • 有一个称为实例的私有变量,这可以满足在任何地方传递/使用数据库的单个实例(只要上下文可用)
    • 一个函数
      getInstance
      返回实例,仅实例化一次。
    • 在实例化实例之前,通过调用
      ifDBExists
      函数检查数据库是否存在。
      • 如果不存在,则调用
        getDBFromAsset
        函数来执行它所说的操作。 (注意如何将其读入 ByteArray(Room 中的 BLOB))
      • 如果确实存在,则完成注释,继续处理
    • 由于数据库现已存在,因此
      instance
      变量将被实例化并返回。
  • 应该注意的是,即使重写的
    onCreate
    函数将创建唯一的表,但实际上永远不应该调用该函数,因为数据库是资产中预先填充/预先存在的数据库。

另外还创建了另一个类

PDFReader
:-

class PDFReader() {
    fun doesPDFExist(pdfName: String, context: Context): Boolean {
        try {
            val pdf = context.assets.open(pdfName)
            val o = context.getDatabasePath(pdfName).outputStream()
            pdf.copyTo(o)
        } catch (e: Exception) {
            e.printStackTrace()
            return false
        }
        return true
    }
}
  • 此类只是为了演示对 PDF 资源的访问,方法是将它们复制到存储数据库的同一文件夹中(如果需要,覆盖它们)。 无意显示 PDF 超出答案范围

显然,如果不使用上述类,什么也不会完成,因此一些活动代码MainActivity:-

class MainActivity : AppCompatActivity() {
    lateinit var db: DBHelper
    lateinit var csr: Cursor
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        /* Get the database instance */
        db = DBHelper.getInstance(this)
        /* extract the data from the database*/
        csr = db.getAllFromPDF()
        val id_ix = csr.getColumnIndex(PDF_ID_COLUMN)
        val title_ix = csr.getColumnIndex(PDF_TITLE_COLUMN)
        val name_ix = csr.getColumnIndex(PDF_NAME_COLUMN)
        /* for each row copy the file from the asset to the databases folder */
        while (csr.moveToNext()) {
            val r = PDFReader().doesPDFExist(csr.getString(name_ix),this)
        }
        csr.close()
    }
}

运行时然后使用应用程序检查:-

即mypdfstore.db 数据库存在,并且具有根据 SQLite 工具中创建的原始数据库的数据。此外,这 2 个 pdf 文件存在于同一目录中(请注意,为了演示的简洁性,使用了数据库目录)。

使用设备资源管理器:-

即文件存在并且 pdf 文件是预期大小。该数据库有两个附加文件 mypdfstore.db-wal 和 mypdfstore.db-shm;这些在使用 WAL 日志记录时存在。

© www.soinside.com 2019 - 2024. All rights reserved.