Android 图片视频存储和读取

Posted Daniel_Oliva

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 图片视频存储和读取相关的知识,希望对你有一定的参考价值。

1. 应用专属文件

1.1 文件路径

        应用专属文件的访问与更新不需要任何权限。

        应用专属文件包括应用内部空间和应用外部空间。以下是内外部空间对应的目录获取方式。其中`getExternalFilesDir`可以设置对应目录下的文件夹,我们可以使用Environment中设置的变量来指定文件夹,防止搞混。

        这些文件会在应用卸载的时候删除。

        内部空间中的文件其他应用无法访问。外部空间中的文件,其他应用在适当权限下可以访问。

Context.getExternalCacheDir() 
/storage/emulated/0/android/data/com.example.myapplication/cache

Context.getExternalFilesDir(null)
/storage/emulated/0/Android/data/com.example.myapplication/files

Context.getExternalFilesDir("abcdefg")
/storage/emulated/0/Android/data/com.example.myapplication/files/abcdefg

Context.getCacheDir()
/data/user/0/com.example.myapplication/cache

Context.getFilesDir()
/data/user/0/com.example.myapplication/files

Environment.DIRECTORY_MUSIC = "Music"
Environment.DIRECTORY_PICTURES = "Pictures"
Environment.DIRECTORY_DOWNLOADS = "Download"
Environment.DIRECTORY_DOCUMENTS = "Documents"
Environment.DIRECTORY_SCREENSHOTS = "Screenshots"
Environment.DIRECTORY_MOVIES = "Movies"

         还可以获取外部存储的根目录,对文件进行操作。

Environment.getExternalStorageDirectory();
/storage/emulated/0

Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) // 必须带个目录名字符串
/storage/emulated/0/Pictures

1.2 文件访问

  1. 使用File API
  2. context.openFileOutput(filename, Context.MODE_...) 获取FileOutStream
  3. context.openFileInput() 获取FileInputStream
  4. 使用文件描述符来访问文件。(如bitmap的生成就可以使用文件描述符)
    ParcelFileDescriptor parcelFileDescriptor =
    getContentResolver().openFileDescriptor(uri, "r");
    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();

2. 共享空间

        共享空间主要使用ContentResolver + MediaStore API来进行访问。

2.1 权限请求

        对于Android10(sdk 29) 以上系统并且开启了分区存储的情况下,如果只是读写自身创建的文件,则不需要请求任何权限。如果需要访问其他应用创建的文件,则需要请求以下权限。(一定不要请求WRITE_EXTERNAL_STORAGE权限,因为对于Android10以上的系统并没有这个权限。)

Manifest.permission.READ_EXTERNAL_STORAGE

        对于Android9及以下的系统,则需要请求下个两个权限。

Manifest.permission.WRITE_EXTERNAL_STORAGE
Manifest.permission.READ_EXTERNAL_STORAGE

 2.2 MediaStoreAPI 访问媒体

2.2.1 查询 

Cursor query = contentResolver.query(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                new String[]
                        MediaStore.Images.Media._ID,
                        MediaStore.Images.Media.DATA,
                        MediaStore.Images.Media.DISPLAY_NAME,
                        MediaStore.Images.Media.SIZE
                ,
                MediaStore.Images.Media.DATA + " LIKE ?",
                new String[]"%Screenshot_2022-06-18-10-24%",
                null
);

while (query.moveToNext()) 
    String id = query.getString(query.getColumnIndex(MediaStore.Images.Media._ID));
    String DATA = query.getString(query.getColumnIndex(MediaStore.Images.Media.DATA));
    String DISPLAY_NAME = query.getString(query.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
    String EXPOSURE_TIME = query.getString(query.getColumnIndex(MediaStore.Images.Media.SIZE));
    uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, Long.parseLong(id));

2.2.2  插入

        值得注意的是,使用contentResolve.openOutputStream、contentResolve.openInputStream来根据提供的uri来对文件进行读写。

// 1. 插入个空文件占个坑
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "hello.jpg");
values.put(MediaStore.Images.Media.AUTHOR, "李嘉浩");

Uri insert = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

try 
    // 2. 向文件写入数据
    OutputStream outputStream = contentResolver.openOutputStream(insert);
    InputStream inputStream = contentResolver.openInputStream(uri); // 这个uri是上面查询部分获取的某一张图片uri。
    byte[] buffer = new byte[1024];
    int byteNum = 0;
    while ((byteNum = inputStream.read(buffer, 0, buffer.length)) != -1) 
        outputStream.write(buffer, 0, byteNum);
    
 catch (FileNotFoundException e) 
    e.printStackTrace();
 catch (IOException e) 
    e.printStackTrace();

参考

官网https://developer.android.com/training/data-storage

Android 11 拍照+录制视频保存到外部共享区域

本文是 Android 11 从外部存储读取文件到应用沙盒存储  和 Android 11 从沙盒拷贝文件到外部共享存储区域 实践篇。

 项目中我们有拍摄图片或者录制视频保存到外部共享存储区域的需求。

Android 10之前,我们可以直接new File的形式访问外部存储区域的任意路径。

Android 10后,Google推出了沙盒机制,应用通过new File形式访问只能访问自己沙盒下路径;可以通过ContentResolver访问外部共享存储区域。

外部共享存储区域:主要是指Enviromnent下的 如

    Environment.DIRECTORY_DCIM
    Environment.DIRECTORY_DOCUMENTS
    Environment.DIRECTORY_DOWNLOADS
    Environment.DIRECTORY_MOVIES
    Environment.DIRECTORY_PICTURES
    Environment.DIRECTORY_MUSIC

等。
 

本文是关于调用系统拍照+录制视频并且保存到外部共享存储区域的demo,效果:

核心实现: 

 1. 调用系统拍照和录制视频的方法requestWriteImage()和requestWriteVideo()

 2. 拍照和录制视频保存到外部共享存储,android Q以上 需要通过ContentResolver获取uri :getImageUriAboveQ()和getVideoUriAboveQ()

public void requestWriteImage() 
        Intent intent = new Intent();
        intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
        Uri mediaUri = null;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) 
            mediaUri = getImgeUriAboveQ(mFragment.getActivity(), "Image_" + System.currentTimeMillis());
         else 
            File dicmFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
            File mediaFile = new File(dicmFile.getAbsoluteFile() + "Image_" +  System.currentTimeMillis() +".jpg");
            mediaUri = Uri.fromFile(mediaFile);
        
        intent.putExtra(MediaStore.EXTRA_OUTPUT, mediaUri);
        mFragment.startActivityForResult(intent, REQUEST_CODE_WRITE_IMAGE);
    

    public void requestWriteVideo() 
        Intent intent = new Intent();
        intent.setAction("android.media.action.VIDEO_CAPTURE");
        intent.addCategory("android.intent.category.DEFAULT");
        Uri mediaUri = null;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) 
            mediaUri = getVideoUriAboveQ(mFragment.getActivity(), "Video_" + System.currentTimeMillis());
         else 
            File dicmFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
            File mediaFile = new File(dicmFile.getAbsoluteFile() + "Video_" +  System.currentTimeMillis() +".mp4");
            mediaUri = Uri.fromFile(mediaFile);
        
        intent.putExtra(MediaStore.EXTRA_OUTPUT, mediaUri);
        mFragment.startActivityForResult(intent, REQUEST_CODE_WRITE_VIDEO);
    

    private Uri getImgeUriAboveQ(Context context, String imageName) 
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) 
            return null;
        
        Uri imageUri = null;
        ContentResolver resolver = context.getContentResolver();
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, imageName);//图片名
        values.put(MediaStore.Images.Media.DESCRIPTION, imageName); //描述
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");//类型
        values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM);//保存路径
        Uri externalUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        imageUri = resolver.insert(externalUri, values);
        return imageUri;
    

    private Uri getVideoUriAboveQ(Context context, String videoName) 
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) 
            return null;
        
        Uri videoUri = null;
        ContentResolver resolver = context.getContentResolver();
        ContentValues values = new ContentValues();
        values.put(MediaStore.Video.Media.DISPLAY_NAME, videoName);//视频名
        values.put(MediaStore.Video.Media.DESCRIPTION, videoName); //描述
        values.put(MediaStore.Images.Media.MIME_TYPE, "video/mp4");//类型
        values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM);//保存路径
        Uri externalUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
        videoUri = resolver.insert(externalUri, values);
        return videoUri;
    

3. 弹BottomSheetDialog调用requestWriteImage()和requestWriteVideo()

 public void showBottomSheetDialog() 
        bottomSheetDialog = new BottomSheetDialog(mFragment.getActivity());
        View dialogView= LayoutInflater.from(mFragment.getActivity())
                .inflate(R.layout.layout_bottom_sheet_image_video, null);
        TextView pictureTv= (TextView) dialogView.findViewById(R.id.picture_tv);
        TextView videoTv= (TextView) dialogView.findViewById(R.id.video_tv);
        TextView cancelTv= (TextView) dialogView.findViewById(R.id.cancel_tv);
        pictureTv.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                requestWriteImage();
                bottomSheetDialog.dismiss();
            
        );
        videoTv.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                requestWriteVideo();
                bottomSheetDialog.dismiss();
            
        );

        cancelTv.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                bottomSheetDialog.dismiss();
            
        );

        bottomSheetDialog.setContentView(dialogView);
        bottomSheetDialog.show();
    

Demo地址:

CODING | 一站式软件研发管理平台

以上是关于Android 图片视频存储和读取的主要内容,如果未能解决你的问题,请参考以下文章

Android 11 拍照+录制视频保存到外部共享区域

android 存储图片到data目录和读取data目录下的图片

android怎么用流 读取云端图片

Android14.2 外部文件存储和读取

android contentprovider 有啥用

Android笔记:AlbumSaver图片视频保存工具类