• android 11及以上保存图片视频到相册


    Android 10之前版本主要步骤

    1. 请求读写权限
    2. 图片/视频下载到/storage/emulated/0/Android/data/包名/xxx
    3. 复制到系统相册目录下
    4. 扫描媒体库

    Android 10及以上版本主要步骤

    1. 请求读写权限
    2. 图片/视频下载到/storage/emulated/0/Android/data/包名/xxx
    3. 创建ContentValues,写入要保存的信息
    4. 调用ContentResolver插入ContentValues到相册中,此时会返回新创建的相册uri
    5. 将原先的文件复制到该uri中(android11及以上必须这么干)
    6. 发送广播,扫描媒体库

    关键代码:

    1. public class SaveUtils {
    2. private static final String TAG = "SaveUtils";
    3. /**
    4. * 将图片文件保存到系统相册
    5. */
    6. public static boolean saveImgFileToAlbum(Context context, String imageFilePath) {
    7. Log.d(TAG, "saveImgToAlbum() imageFile = [" + imageFilePath + "]");
    8. try {
    9. Bitmap bitmap = BitmapFactory.decodeFile(imageFilePath);
    10. return saveBitmapToAlbum(context, bitmap);
    11. } catch (Exception e) {
    12. e.printStackTrace();
    13. return false;
    14. }
    15. }
    16. /**
    17. * 将bitmap保存到系统相册
    18. */
    19. public static boolean saveBitmapToAlbum(Context context, Bitmap bitmap) {
    20. if (bitmap == null) return false;
    21. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
    22. return saveBitmapToAlbumBeforeQ(context, bitmap);
    23. } else {
    24. return saveBitmapToAlbumAfterQ(context, bitmap);
    25. }
    26. }
    27. @RequiresApi(api = Build.VERSION_CODES.Q)
    28. private static boolean saveBitmapToAlbumAfterQ(Context context, Bitmap bitmap) {
    29. Uri contentUri;
    30. if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    31. contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    32. } else {
    33. contentUri = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
    34. }
    35. ContentValues contentValues = getImageContentValues(context);
    36. Uri uri = context.getContentResolver().insert(contentUri, contentValues);
    37. if (uri == null) {
    38. return false;
    39. }
    40. OutputStream os = null;
    41. try {
    42. os = context.getContentResolver().openOutputStream(uri);
    43. bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);
    44. // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    45. // Files.copy(bitmapFile.toPath(), os);
    46. // }
    47. contentValues.clear();
    48. contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
    49. context.getContentResolver().update(uri, contentValues, null, null);
    50. return true;
    51. } catch (Exception e) {
    52. context.getContentResolver().delete(uri, null, null);
    53. e.printStackTrace();
    54. return false;
    55. } finally {
    56. try {
    57. if (os != null) {
    58. os.close();
    59. }
    60. } catch (IOException e) {
    61. e.printStackTrace();
    62. }
    63. }
    64. }
    65. private static boolean saveBitmapToAlbumBeforeQ(Context context, Bitmap bitmap) {
    66. File picDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
    67. File destFile = new File(picDir, context.getPackageName() + File.separator + System.currentTimeMillis() + ".jpg");
    68. // FileUtils.copy(imageFile, destFile.getAbsolutePath());
    69. OutputStream os = null;
    70. boolean result = false;
    71. try {
    72. if (!destFile.exists()) {
    73. destFile.getParentFile().mkdirs();
    74. destFile.createNewFile();
    75. }
    76. os = new BufferedOutputStream(new FileOutputStream(destFile));
    77. result = bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);
    78. if (!bitmap.isRecycled()) bitmap.recycle();
    79. } catch (IOException e) {
    80. e.printStackTrace();
    81. } finally {
    82. try {
    83. if (os != null) {
    84. os.close();
    85. }
    86. } catch (IOException e) {
    87. e.printStackTrace();
    88. }
    89. }
    90. MediaScannerConnection.scanFile(
    91. context,
    92. new String[]{destFile.getAbsolutePath()},
    93. new String[]{"image/*"},
    94. (path, uri) -> {
    95. Log.d(TAG, "saveImgToAlbum: " + path + " " + uri);
    96. // Scan Completed
    97. });
    98. return result;
    99. }
    100. /**
    101. * 获取图片的ContentValue
    102. *
    103. * @param context
    104. */
    105. @RequiresApi(api = Build.VERSION_CODES.Q)
    106. public static ContentValues getImageContentValues(Context context) {
    107. ContentValues contentValues = new ContentValues();
    108. contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, System.currentTimeMillis() + ".jpg");
    109. contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/*");
    110. contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + File.separator + context.getPackageName());
    111. contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1);
    112. contentValues.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
    113. contentValues.put(MediaStore.Images.Media.DATE_MODIFIED, System.currentTimeMillis());
    114. contentValues.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis());
    115. return contentValues;
    116. }
    117. /**
    118. * 将视频保存到系统相册
    119. */
    120. public static boolean saveVideoToAlbum(Context context, String videoFile) {
    121. Log.d(TAG, "saveVideoToAlbum() videoFile = [" + videoFile + "]");
    122. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
    123. return saveVideoToAlbumBeforeQ(context, videoFile);
    124. } else {
    125. return saveVideoToAlbumAfterQ(context, videoFile);
    126. }
    127. }
    128. private static boolean saveVideoToAlbumAfterQ(Context context, String videoFile) {
    129. try {
    130. ContentResolver contentResolver = context.getContentResolver();
    131. File tempFile = new File(videoFile);
    132. ContentValues contentValues = getVideoContentValues(context, tempFile, System.currentTimeMillis());
    133. Uri uri = contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues);
    134. copyFileAfterQ(context, contentResolver, tempFile, uri);
    135. contentValues.clear();
    136. contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
    137. context.getContentResolver().update(uri, contentValues, null, null);
    138. context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
    139. return true;
    140. } catch (Exception e) {
    141. e.printStackTrace();
    142. return false;
    143. }
    144. }
    145. private static boolean saveVideoToAlbumBeforeQ(Context context, String videoFile) {
    146. File picDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
    147. File tempFile = new File(videoFile);
    148. File destFile = new File(picDir, context.getPackageName() + File.separator + tempFile.getName());
    149. FileInputStream ins = null;
    150. BufferedOutputStream ous = null;
    151. try {
    152. ins = new FileInputStream(tempFile);
    153. ous = new BufferedOutputStream(new FileOutputStream(destFile));
    154. long nread = 0L;
    155. byte[] buf = new byte[1024];
    156. int n;
    157. while ((n = ins.read(buf)) > 0) {
    158. ous.write(buf, 0, n);
    159. nread += n;
    160. }
    161. MediaScannerConnection.scanFile(
    162. context,
    163. new String[]{destFile.getAbsolutePath()},
    164. new String[]{"video/*"},
    165. (path, uri) -> {
    166. Log.d(TAG, "saveVideoToAlbum: " + path + " " + uri);
    167. // Scan Completed
    168. });
    169. return true;
    170. } catch (Exception e) {
    171. e.printStackTrace();
    172. return false;
    173. } finally {
    174. try {
    175. if (ins != null) {
    176. ins.close();
    177. }
    178. if (ous != null) {
    179. ous.close();
    180. }
    181. } catch (IOException e) {
    182. e.printStackTrace();
    183. }
    184. }
    185. }
    186. private static void copyFileAfterQ(Context context, ContentResolver localContentResolver, File tempFile, Uri localUri) throws IOException {
    187. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
    188. context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.Q) {
    189. //拷贝文件到相册的uri,android10及以上得这么干,否则不会显示。可以参考ScreenMediaRecorder的save方法
    190. OutputStream os = localContentResolver.openOutputStream(localUri);
    191. Files.copy(tempFile.toPath(), os);
    192. os.close();
    193. tempFile.delete();
    194. }
    195. }
    196. /**
    197. * 获取视频的contentValue
    198. */
    199. public static ContentValues getVideoContentValues(Context context, File paramFile, long timestamp) {
    200. ContentValues localContentValues = new ContentValues();
    201. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    202. localContentValues.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM
    203. + File.separator + context.getPackageName());
    204. }
    205. localContentValues.put(MediaStore.Video.Media.TITLE, paramFile.getName());
    206. localContentValues.put(MediaStore.Video.Media.DISPLAY_NAME, paramFile.getName());
    207. localContentValues.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
    208. localContentValues.put(MediaStore.Video.Media.DATE_TAKEN, timestamp);
    209. localContentValues.put(MediaStore.Video.Media.DATE_MODIFIED, timestamp);
    210. localContentValues.put(MediaStore.Video.Media.DATE_ADDED, timestamp);
    211. localContentValues.put(MediaStore.Video.Media.SIZE, paramFile.length());
    212. return localContentValues;
    213. }
    214. }



     

  • 相关阅读:
    压力测试-Locust框架基本使用及更新报错解决方案
    什么是哲学?《哲学家们都干了些什么?》读后感
    Android网络通讯之Retrofit
    Vue2实现图片预览功能 -- v-viewer:图片查看器
    Electronica上海 Samtec 验证演示 | FireFly™Micro Flyover System™
    不同网段的IP怎么互通
    锥坡 锥坡 锥坡
    C#学习笔记(2)——program building blocks
    PHP7和PHP8的新特性
    K8s上的监控系统(Grafana)使用和理解说明
  • 原文地址:https://blog.csdn.net/yzwfeng/article/details/127768671