targetSdkVersion > 30 如何将下载的网络视频 保存到手机相册里更新

targetSdkVersion 31 中,将下载的网络视频保存到手机相册中涉及几个关键步骤。由于 Android 12(API 级别 31)引入了更多的隐私和安全限制,特别是作用域存储(Scoped Storage),因此你需要遵循这些限制来保存文件。

以下是将下载的网络视频保存到手机相册的步骤:

  1. 请求必要的权限
    • 对于网络下载,你可能需要 INTERNET 权限。
    • 对于保存到外部存储,你需要考虑作用域存储的限制。如果你的应用面向的是 Android 10(API 级别 29)及以上版本,并且你的应用没有声明 requestLegacyExternalStorage 属性为 true,则你需要使用 MediaStore API 来保存文件,并且不需要 WRITE_EXTERNAL_STORAGE 权限。
  2. 下载视频文件
    • 使用适当的网络库(如 OkHttp, Retrofit, Volley 等)从网络下载视频文件。
    • 将下载的视频文件保存到应用的私有目录(例如,使用 getExternalFilesDir(null)getCacheDir())。
  3. 使用 MediaStore 保存视频到相册
    • 创建一个 ContentValues 对象来存储视频文件的元数据。
    • 使用 MediaStore.Video.Media.getExternalContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) 获取外部存储中视频内容的 URI。
    • 调用 ContentResolver.insert() 方法来创建一个新的视频条目。
    • 使用 ContentResolver.openOutputStream() 获取输出流,并将下载的视频文件数据写入该输出流。
  4. 更新媒体数据库
    • 在视频文件写入后,你可能需要通知媒体扫描器扫描该文件,以便它出现在相册中。这可以通过发送一个 Intent 来实现,使用 MediaScannerConnection.SCAN_FILE_ACTION

下面是一个简化的代码示例,展示了如何实现这一过程:

java 复制代码
// 假设你已经从网络下载了视频,并且将其保存在了 privateDir 路径下  
File videoFile = new File(privateDir, "downloaded_video.mp4");  
  
// 使用 MediaStore API 保存视频到外部存储  
ContentValues values = new ContentValues();  
values.put(MediaStore.Video.Media.DISPLAY_NAME, videoFile.getName());  
values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");  
values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES);  
  
Uri collection = MediaStore.Video.Media.getExternalContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);  
Uri videoUri = getContentResolver().insert(collection, values);  
  
try (OutputStream outputStream = getContentResolver().openOutputStream(videoUri)) {  
    Files.copy(videoFile.toPath(), outputStream);  
} catch (IOException e) {  
    e.printStackTrace();  
}  
  
// 通知媒体扫描器扫描新文件  
MediaScannerConnection.scanFile(this,  
        new String[]{videoFile.toString()},  
        new String[]{"video/*"},  
        null);

请注意,由于作用域存储的限制,你可能无法直接将文件保存到公共目录(如 Environment.DIRECTORY_MOVIES)。如果你的应用没有请求 MANAGE_EXTERNAL_STORAGE 权限,或者用户没有授予该权限,你可能需要将文件保存到应用的私有目录,并通过 MediaStore 创建一个指向该文件的引用。

此外,始终确保你的应用遵循最新的 Android 存储最佳实践,并考虑到用户隐私和数据安全。

另外

在 Android 10(API 级别 29)及以上版本,由于作用域存储(Scoped Storage)的引入,直接获取文件在外部存储的完整路径变得不再直接可行。相反,您应该使用 ContentResolverContentValues 将文件保存到 MediaStore,并通过返回的 Uri 来引用它。这样,视频文件就可以被其他应用(如相册)访问,而不需要知道其实际存储路径。

以下是一个示例,展示了如何将下载的视频保存到 MediaStore 并获取其 Uri

java 复制代码
private Uri saveVideoToMediaStore(Context context, File videoFile) throws IOException {  
    // 创建一个 ContentValues 对象来存储视频文件的元数据  
    ContentValues values = new ContentValues();  
    values.put(MediaStore.Video.Media.DISPLAY_NAME, videoFile.getName());  
    values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");  
    values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES);  
  
    // 获取外部存储中视频内容的 Uri  
    Uri collection = MediaStore.Video.Media.getExternalContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);  
  
    // 使用 ContentResolver 插入一个新的视频条目到 MediaStore  
    Uri videoUri = context.getContentResolver().insert(collection, values);  
  
    // 使用 ContentResolver 打开一个输出流来写入视频文件  
    try (OutputStream outputStream = context.getContentResolver().openOutputStream(videoUri)) {  
        Files.copy(videoFile.toPath(), outputStream);  
    }  
  
    // 通知媒体扫描器扫描新文件,使其出现在相册中  
    MediaScannerConnection.scanFile(context,  
            new String[]{videoFile.getAbsolutePath()},  
            new String[]{"video/*"},  
            null);  
  
    return videoUri;  
}

在这个示例中,videoUri 就是您需要的 Uri,它代表了保存在 MediaStore 中的视频文件。您可以使用这个 Uri 来在其他地方引用该视频,例如在一个 Intent 中传递给相册应用。

请注意,由于作用域存储的限制,您无法直接从这个 Uri 获取文件的完整路径。如果您需要这个路径用于某些特定的操作(比如在其他应用中分享文件),您可能需要使用 FileProvider 来创建一个临时访问权限的 Uri

如果您确实需要获取视频文件的路径用于某些特殊情况(比如备份到云存储),您可以使用 ContentResolveropenFileDescriptor 方法来获取一个 ParcelFileDescriptor,然后从中获取文件描述符,并最终转换为 File 对象。但请注意,这样做可能会违反 Android 的隐私政策,因此应该谨慎使用,并确保您的应用遵循了所有的最佳实践和隐私指南。

如果您的应用需要兼容低版本的 Android(低于 API 级别 29),您可能需要采用不同的方法来保存视频到外部存储,因为作用域存储(Scoped Storage)在这些版本上不可用。在这种情况下,您可能需要请求 WRITE_EXTERNAL_STORAGE 权限,并将文件直接保存到公共目录(如 /sdcard/DCIM/Camera/)中。

以下是一个兼容低版本的示例,展示如何保存视频到外部存储并获取其路径:

java 复制代码
private String saveVideoToExternalStorage(Context context, File videoFile) {  
    // 确保有写入外部存储的权限  
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {  
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)  
                != PackageManager.PERMISSION_GRANTED) {  
            // 请求写入外部存储的权限  
            ActivityCompat.requestPermissions(  
                    (Activity) context,  
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},  
                    MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);  
            // 等待用户授权  
            return null;  
        }  
    } else {  
        // 对于低于 Android 10 的版本,您可以在 AndroidManifest.xml 中声明权限  
    }  
  
    // 获取外部存储的目录  
    File externalStorageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);  
    if (!externalStorageDir.exists()) {  
        if (!externalStorageDir.mkdirs()) {  
            Log.e("VideoSave", "Failed to create directory");  
            return null;  
        }  
    }  
  
    // 将视频文件复制到外部存储的目录  
    File destinationFile = new File(externalStorageDir, videoFile.getName());  
    try {  
        Files.copy(videoFile.toPath(), destinationFile.toPath());  
        return destinationFile.getAbsolutePath();  
    } catch (IOException e) {  
        e.printStackTrace();  
        return null;  
    }  
}

在这个示例中,我们首先检查应用是否有写入外部存储的权限。如果没有,我们会请求这个权限。一旦获得权限,我们就将视频文件复制到外部存储的 /sdcard/Movies/ 目录下,并返回文件的绝对路径。

请注意,从 Android 10(API 级别 29)开始,推荐的做法是使用 MediaStore API,因为它提供了更好的隐私控制和兼容性。如果您的应用需要支持 Android 10 及更高版本,强烈建议使用 MediaStore 方法。如果您的应用需要兼容低版本,并且您确定您的用户主要使用这些低版本设备,那么您可以使用上述方法。但请记住,随着时间的推移,低版本设备的用户群体可能会逐渐减少,因此最好尽可能使用最新的 API 和实践。

相关推荐
走在路上的菜鸟14 小时前
Android学Dart学习笔记第四节 基本类型
android·笔记·学习
百锦再14 小时前
第21章 构建命令行工具
android·java·图像处理·python·计算机视觉·rust·django
skyhh16 小时前
Android Studio 最新版汉化
android·ide·android studio
路人甲ing..16 小时前
Android Studio 快速的制作一个可以在 手机上跑的app
android·java·linux·智能手机·android studio
携欢19 小时前
PortSwigger靶场之Web shell upload via path traversal靶场通关秘籍
android
消失的旧时光-19431 天前
Android ADB指令大全详解
android·adb
ashcn20011 天前
opengl 播放视频的android c++ 方案
android·c++ opengl es
abner.Li1 天前
android 反编译
android
Digitally1 天前
如何删除 realme 手机上的短信
android
2501_916008891 天前
提高 iOS 应用逆向难度的工程实践,多工具联动的全栈安全方案
android·安全·ios·小程序·uni-app·cocoa·iphone