在Android中存储图片到数据库时,需根据需求选择合适方案,以下是常见存储方式的对比:
存储方式 | 优点 | 缺点 |
---|---|---|
直接存Bitmap | 简单快速,无需额外文件管理 | 占用数据库空间大,可能导致性能问题;不支持跨应用共享 |
存文件路径 | 节省数据库空间,支持大图存储 | 需管理文件系统,路径可能因清理/迁移丢失 |
Base64编码 | 数据统一存储,避免文件管理 | 编码后数据量增大(约33%),解码耗时 |
URI存储 | 兼容Android 10+分区存储,安全性高 | 需配合ContentResolver使用,逻辑稍复杂 |
推荐使用Room持久化库,其优势包括:
@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 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(); } }
// 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); }
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
// 在Activity中请求权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_STORAGE);
MediaStore
API替代文件路径存储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);
// 加载缩略图 Glide.with(context).load(entity.thumbnail) .placeholder(R.drawable.placeholder) .into(imageView);
path
字段)AsyncTask
或WorkManager
处理IO密集型任务PagingSource
实现懒加载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值即可判断重复。
A2:可结合AES加密和Base64编码:
encryptedData = AESUtils.encrypt(imageBytes)
Bitmap bitmap = AESUtils.decryptToBitmap(base64Data)