当前位置:首页 > 行业动态 > 正文

安卓开发之数据库存取图片

图片存储方案对比

在Android中存储图片到数据库时,需根据需求选择合适方案,以下是常见存储方式的对比:

存储方式 优点 缺点
直接存Bitmap 简单快速,无需额外文件管理 占用数据库空间大,可能导致性能问题;不支持跨应用共享
存文件路径 节省数据库空间,支持大图存储 需管理文件系统,路径可能因清理/迁移丢失
Base64编码 数据统一存储,避免文件管理 编码后数据量增大(约33%),解码耗时
URI存储 兼容Android 10+分区存储,安全性高 需配合ContentResolver使用,逻辑稍复杂

数据库设计与选型

推荐使用Room持久化库,其优势包括:

安卓开发之数据库存取图片

安卓开发之数据库存取图片

  • 编译时校验SQL语句
  • 支持LiveData实时监听
  • 自动生成Dao实现类

实体类定义(以存储图片路径为例)

@Entity(tableName = "images")
public class ImageEntity {
    @PrimaryKey(autoGenerate = true)
    public int id;
    @ColumnInfo(name = "image_path")
    public String path; // 存储图片在外部存储的绝对路径
    @ColumnInfo(name = "thumbnail")
    public String thumbnail; // 可选:存储缩略图Base64(用于列表展示)
}

Dao接口定义

@Dao
public interface ImageDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(ImageEntity entity);
    @Query("SELECT  FROM images WHERE id = :id")
    LiveData<ImageEntity> getById(int id);
    @Query("SELECT  FROM images")
    LiveData<List<ImageEntity>> getAll();
}

关键实现步骤

图片压缩与存储

// 压缩图片并保存到外部存储
public File saveImage(Bitmap bitmap, String dirName) throws IOException {
    String filename = System.currentTimeMillis() + ".jpg";
    File dir = new File(Environment.getExternalStoragePublicDirectory(dirName), "Android/data");
    if (!dir.exists()) dir.mkdirs();
    File file = new File(dir, filename);
    // 压缩质量设为80,平衡大小与清晰度
    Bitmap.createScaledBitmap(bitmap, 800, 1200, false).compress(Bitmap.CompressFormat.JPEG, 80, new FileOutputStream(file));
    return file;
}

数据库插入操作

// 在Repository中处理存储逻辑
public void saveImage(Bitmap bitmap) {
    try {
        File imageFile = saveImage(bitmap, Environment.DIRECTORY_PICTURES);
        ImageEntity entity = new ImageEntity();
        entity.path = imageFile.getAbsolutePath();
        // 生成缩略图(宽200px)
        Bitmap thumb = Bitmap.createScaledBitmap(bitmap, 200, (int)(200bitmap.getHeight()/(float)bitmap.getWidth()), false);
        entity.thumbnail = bitmapToBase64(thumb); // 缩略图转Base64
        imageDao.insert(entity);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Base64转换工具

// Bitmap与Base64互转
public String bitmapToBase64(Bitmap bitmap) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
    return Base64.encodeToString(baos.toByteArray(), Base64.NO_WRAP);
}
public Bitmap base64ToBitmap(String base64) {
    byte[] bytes = Base64.decode(base64, Base64.DEFAULT);
    return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
}

权限与兼容性处理

AndroidManifest声明

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

运行时权限申请(针对Android 6.0+)

// 在Activity中请求权限
ActivityCompat.requestPermissions(this, 
    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_STORAGE);

适配Android 10+分区存储

  • 使用MediaStoreAPI替代文件路径存储
  • 示例:通过ContentValues插入图片到系统图库
    ContentValues values = new ContentValues();
    values.put(MediaStore.Images.Media.DISPLAY_NAME, "image_"+System.currentTimeMillis()+".jpg");
    values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
    Uri uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

常见问题与优化

大图导致的OOM问题

  • 解决方案:先加载缩略图,点击查看大图时再加载原图
  • 代码示例
    // 加载缩略图
    Glide.with(context).load(entity.thumbnail)
      .placeholder(R.drawable.placeholder)
      .into(imageView);

数据库性能优化

  • 索引优化:对频繁查询字段建立索引(如path字段)
  • 异步操作:使用AsyncTaskWorkManager处理IO密集型任务
  • 分页查询:结合PagingSource实现懒加载

相关问题与解答

Q1:如何判断图片是否已存在数据库?

A1:可通过文件MD5校验或路径匹配实现,推荐使用MD5校验:

// 计算文件MD5
public String getFileMD5(File file) throws Exception {
    MessageDigest digest = MessageDigest.getInstance("MD5");
    try(InputStream is = new FileInputStream(file)) {
        byte[] buffer = new byte[1024];
        int read;
        while ((read = is.read(buffer)) != -1) {
            digest.update(buffer, 0, read);
        }
    }
    byte[] md5Bytes = digest.digest();
    // 转换为16进制字符串
    StringBuilder hexString = new StringBuilder();
    for (byte b : md5Bytes) hexString.append(String.format("%02x", b));
    return hexString.toString();
}

查询数据库时比对MD5值即可判断重复。

安卓开发之数据库存取图片

Q2:如何实现图片的加密存储?

A2:可结合AES加密和Base64编码:

  1. 加密原始图片数据:encryptedData = AESUtils.encrypt(imageBytes)
  2. 将加密后的数据转为Base64存储到数据库
  3. 读取时反向解密:Bitmap bitmap = AESUtils.decryptToBitmap(base64Data)
    需注意密钥管理,建议使用Android Keystore系统存储密钥