You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何从手机内部存储选取PDF存入SQLite并读取打开

搞定这个需求很简单,你已经迈出了第一步——选取PDF文件,接下来咱们一步步完成将PDF转为Blob存入SQLite从数据库取出并打开的功能:

1. 完善文件选取后的处理:将PDF转为Blob

你已经通过ACTION_GET_CONTENT获取了PDF的Uri,接下来需要把这个Uri对应的文件转换成字节数组(也就是SQLite能存储的Blob类型),然后存入数据库。

首先补全你的onActivityResult方法:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 100 && resultCode == RESULT_OK && data != null) {
        Uri fileUri = data.getData();
        try {
            // 通过ContentResolver获取文件输入流
            InputStream inputStream = getContentResolver().openInputStream(fileUri);
            // 将输入流转换为字节数组(Blob)
            byte[] pdfBlob = convertInputStreamToByteArray(inputStream);
            
            // 调用方法将Blob存入SQLite
            savePdfToDatabase(pdfBlob);
            
            Toast.makeText(this, "PDF已成功存入数据库", Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(this, "读取PDF文件失败", Toast.LENGTH_SHORT).show();
        }
    }
}

// 辅助方法:把输入流转换成字节数组
private byte[] convertInputStreamToByteArray(InputStream inputStream) throws IOException {
    ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
    int bufferSize = 1024;
    byte[] buffer = new byte[bufferSize];
    
    int len;
    while ((len = inputStream.read(buffer)) != -1) {
        byteBuffer.write(buffer, 0, len);
    }
    return byteBuffer.toByteArray();
}
2. 创建SQLite数据库帮助类,实现Blob存储

接下来需要一个SQLite帮助类来创建表、插入和查询Blob数据:

public class PdfDatabaseHelper extends SQLiteOpenHelper {
    // 数据库和表的常量定义
    private static final String DATABASE_NAME = "PdfStorage.db";
    private static final int DATABASE_VERSION = 1;
    private static final String TABLE_PDFS = "pdfs";
    private static final String COLUMN_ID = "_id";
    private static final String COLUMN_PDF_BLOB = "pdf_blob";

    public PdfDatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // 创建存储PDF Blob的表
        String createTableSql = "CREATE TABLE " + TABLE_PDFS + " (" +
                COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                COLUMN_PDF_BLOB + " BLOB NOT NULL)";
        db.execSQL(createTableSql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 升级时删除旧表(实际项目建议做数据迁移,避免丢失数据)
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_PDFS);
        onCreate(db);
    }

    // 插入PDF Blob到数据库
    public long insertPdfBlob(byte[] pdfBlob) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(COLUMN_PDF_BLOB, pdfBlob);
        
        // 插入数据并返回行ID(可以保存这个ID,后续用来查询对应PDF)
        long rowId = db.insert(TABLE_PDFS, null, values);
        db.close();
        return rowId;
    }

    // 根据行ID查询PDF Blob
    public byte[] getPdfBlobById(long rowId) {
        SQLiteDatabase db = this.getReadableDatabase();
        byte[] pdfBlob = null;
        
        Cursor cursor = db.query(TABLE_PDFS, new String[]{COLUMN_PDF_BLOB},
                COLUMN_ID + "=?", new String[]{String.valueOf(rowId)},
                null, null, null);
        
        if (cursor != null && cursor.moveToFirst()) {
            pdfBlob = cursor.getBlob(cursor.getColumnIndexOrThrow(COLUMN_PDF_BLOB));
            cursor.close();
        }
        db.close();
        return pdfBlob;
    }
}

然后实现刚才调用的savePdfToDatabase方法:

private void savePdfToDatabase(byte[] pdfBlob) {
    PdfDatabaseHelper dbHelper = new PdfDatabaseHelper(this);
    long savedRowId = dbHelper.insertPdfBlob(pdfBlob);
    // 这里可以把savedRowId保存到SharedPreferences或者变量中,方便后续查看时使用
}
3. 实现「查看」按钮功能:从数据库取出Blob并打开PDF

因为第三方PDF应用无法直接读取SQLite里的Blob,所以我们需要把Blob转换成临时文件,再通过Intent打开它。

首先给「查看」按钮添加点击事件:

Button viewPdfBtn = findViewById(R.id.btn_view_pdf);
viewPdfBtn.setOnClickListener(v -> {
    // 这里用之前保存的rowId,示例中用1代替实际ID
    PdfDatabaseHelper dbHelper = new PdfDatabaseHelper(this);
    byte[] pdfBlob = dbHelper.getPdfBlobById(1);
    
    if (pdfBlob != null) {
        openPdfFromBlob(pdfBlob);
    } else {
        Toast.makeText(this, "数据库中未找到对应PDF", Toast.LENGTH_SHORT).show();
    }
});

然后实现openPdfFromBlob方法,处理Blob转临时文件和打开逻辑:

private void openPdfFromBlob(byte[] pdfBlob) {
    try {
        // 在应用缓存目录创建临时PDF文件
        File tempPdfFile = new File(getExternalCacheDir(), "temp_view.pdf");
        FileOutputStream fos = new FileOutputStream(tempPdfFile);
        fos.write(pdfBlob);
        fos.close();
        
        // 构建打开PDF的Intent
        Intent viewIntent = new Intent(Intent.ACTION_VIEW);
        Uri fileUri;
        
        // Android 7.0及以上必须用FileProvider避免FileUriExposedException
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            fileUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", tempPdfFile);
            viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } else {
            fileUri = Uri.fromFile(tempPdfFile);
        }
        
        viewIntent.setDataAndType(fileUri, "application/pdf");
        viewIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
        
        // 检查是否有应用能打开PDF
        if (viewIntent.resolveActivity(getPackageManager()) != null) {
            startActivity(viewIntent);
        } else {
            Toast.makeText(this, "未找到支持打开PDF的应用", Toast.LENGTH_SHORT).show();
        }
        
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "打开PDF失败", Toast.LENGTH_SHORT).show();
    }
}
4. 配置FileProvider(Android 7.0+必备)

为了避免Android 7.0及以上的FileUriExposedException,需要配置FileProvider:

  1. AndroidManifest.xml<application>标签内添加:
<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>
  1. res/xml目录下创建file_paths.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 允许访问应用的外部缓存目录 -->
    <external-cache-path name="external_cache" path="." />
</paths>
注意事项
  • 权限问题:Android 10及以上使用分区存储,无需申请READ_EXTERNAL_STORAGE权限;Android 10以下需要在Manifest中添加权限,并动态申请。
  • 临时文件清理:临时文件会存在应用缓存目录,应用卸载时会自动删除,也可以在合适的时机手动清理。
  • 多文件管理:如果需要存储多个PDF,建议在数据库表中添加文件名、存储时间等字段,方便区分和查询。

内容的提问来源于stack exchange,提问作者Jayashri

火山引擎 最新活动