Android 11 从外部存储读取文件到应用沙盒存储

本文是Android 11 从沙盒拷贝文件到外部共享存储区域 的兄弟篇:Android 11 从外部存储读取文件到应用沙盒存储,效果:

Android10之前,访问外部存储目录即SDCard目录只需要

Environment.getExternalStorageDirectory().getAbsolutePath(),再通过new File()的形式访问。

Android 10 开始,Google建议开发者使用存储访问框架访问外部存储。

使用“存储访问框架”打开文件  |  Android 开发者  |  Android Developers

    new File()的形式只能访问自己应用的沙盒存储路径如:

        Context.getExternalFilesDir():SDCard/Android/data/应用包名/files/ 目录
        Context.getExternalCacheDir(): SDCard/Android/data/应用包名/cache/目录
        Context.getCacheDir():/data/data//cache目录
        Context.getFilesDir():     /data/data//files目录

     等......

Android 11以上,开始强制不能再通过new File()的形式访问外部存储区域了。

1. FileHandlePresenter.java实现。
封装方法:发Intent拉起文件选择器
         文件选择后得到Uri拷贝文件到应用包名目录下
         后续根据自己业务需求操作该拷贝后的文件如重命名,解压等等......
package com.mikel.projectdemo.presenter;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.util.Log;
import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class FileHandlePresenter {
    public static final String TAG = "FileHandlePresenter";
    public static final int PERMISSION_CODE_READ_EXTERNAL = 1;
    public static final int REQUEST_CODE_READ_FILE_FROM_EXTERNAL = 1000;
    private Fragment mFragment;
    public FileHandlePresenter(Fragment fragment) {
        mFragment = fragment;
    }

    /**
     * 打开文件选择器
     */
    public void requestReadExternalStorage() {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        //指定多种类型的文件
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        mFragment.startActivityForResult(Intent.createChooser(intent, "选择文件"), REQUEST_CODE_READ_FILE_FROM_EXTERNAL);
    }

    /**
     * 通过uri拷贝外部存储的文件到自己包名的目录下
     * @param uri
     * @param destFile
     */
    private void copyFieUriToInnerStorage(Uri uri, File destFile) {
        InputStream inputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            inputStream = mFragment.getActivity().getContentResolver().openInputStream(uri);
            if(destFile.exists()) {
                destFile.delete();
            }
            fileOutputStream = new FileOutputStream(destFile);
            byte[] buffer = new byte[4096];
            int redCount;
            while ((redCount = inputStream.read(buffer)) >= 0) {
                fileOutputStream.write(buffer, 0, redCount);
            }
        } catch (Exception e) {
            Log.e(TAG, " copy file uri to inner storage e = " + e.toString());
        } finally {
            try {
                if(fileOutputStream != null) {
                    fileOutputStream.flush();
                    fileOutputStream.getFD().sync();
                    fileOutputStream.close();
                }
                if(inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception e) {
                Log.e(TAG, " close stream e = " + e.toString());
            }
        }
    }

    /**
     * 申请权限回调
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    public void onRequestPermissionsResult(int requestCode, @NonNull @NotNull String[] permissions, @NonNull @NotNull int[] grantResults) {
        if(requestCode == PERMISSION_CODE_READ_EXTERNAL) {
            if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                requestReadExternalStorage();
            }
        }
    }

    /**
     * 文件选择后回调
     * @param requestCode
     * @param resultCode
     * @param data
     */
    public void onActivityResult(int requestCode, int resultCode, @Nullable @org.jetbrains.annotations.Nullable Intent data) {
        if(resultCode == Activity.RESULT_OK) {
            switch (requestCode) {
                case REQUEST_CODE_READ_FILE_FROM_EXTERNAL:
                    try {
                        Uri fileUri = data.getData();
                        File destFile = File.createTempFile("temp", ".tmp", mFragment.getActivity().getCacheDir());
                        Log.d(TAG, " read external storage file = "+ fileUri.toString() + ", dest path = " + destFile.getAbsolutePath());
                        copyFieUriToInnerStorage(fileUri, destFile);
                        //todo 外部存储的文件uri 已变成了 应用包下的文件destFile,后续可以new File操作destFile
                        Toast.makeText(mFragment.getActivity(), "Read External Storage file Success! Save Path = " + destFile.getAbsolutePath(), Toast.LENGTH_LONG).show();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

2. Ui Fragment 负责Ui 展示、调用 FileHandlePresenter和动态权限申请......

package com.mikel.projectdemo.uiframework.subtab;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.mikel.projectdemo.R;
import com.mikel.projectdemo.presenter.FileHandlePresenter;
import org.jetbrains.annotations.NotNull;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;

public class SubTabFragment1 extends Fragment {
    FileHandlePresenter fileHandlePresenter;

    public static SubTabFragment1 build() {
        return new SubTabFragment1();
    }


    @Override
    public View onCreateView(@NonNull @NotNull LayoutInflater inflater, @Nullable @org.jetbrains.annotations.Nullable ViewGroup container, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
        View rootView = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_sub_tab_content1, null, true);
        fileHandlePresenter = new FileHandlePresenter(this);
        initUI(rootView);
        return rootView;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull @NotNull String[] permissions, @NonNull @NotNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        fileHandlePresenter.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable @org.jetbrains.annotations.Nullable Intent data) {
        fileHandlePresenter.onActivityResult(requestCode, resultCode, data);
    }

    private void initUI(View rootView) {
        Button readFileBtn = rootView.findViewById(R.id.read_file_from_external_btn);
        readFileBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /**
                 * android 6.0以上动态权限申请
                 */
                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(getActivity(),
                        Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                    requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, FileHandlePresenter.PERMISSION_CODE_READ_EXTERNAL);
                } else {
                    fileHandlePresenter.requestReadExternalStorage();
                }
            }
        });

        Button writeFileBtn = rootView.findViewById(R.id.write_file_to_external_btn);
        writeFileBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

    }
}

3. 另外不要忘了app工程的AndroidManifest.mxl下需要声明权限 

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

Demo地址:

​​​​​​​GitHub - mikelhm/MikelProjectDemo: Personal Android Demo

​​​​​​MVVM: ViewModel+LiveData+DataBinding+Retrofit+Room+Paging+RxJava 总结与实践(Java实现)_xiaobaaidaba123的专栏-CSDN博客

android 嵌套ViewPager + Fragment实现仿头条UI框架Demo_xiaobaaidaba123的专栏-CSDN博客

Android 使用ViewPager2+ExoPlayer+VideoCache 实现仿抖音视频翻页播放_xiaobaaidaba123的专栏-CSDN博客

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在Mac应用程序中,如果想要在沙盒中写入文件,首先需要了解沙盒是什么。沙盒是一种安全机制,用于限制应用程序的访问权限,确保应用程序只能访问特定的文件和文件夹。因此,在沙盒中写入文件需要遵循一些规则和限制。 首先,需要使用特定的API来进行文件写入操作,例如使用`NSFileManager`类中的方法`createFileAtPath:contents:attributes:`。在使用这些API时,需要提供要写入的文件路径和文件内容,同时需要注意路径是相对于应用程序沙盒的。 其次,由于沙盒的限制,应用程序只有读取和写入自己的沙盒中的文件的权限,无法直接读取和写入其他应用程序或系统文件。这意味着,如果想要在沙盒中写入文件,需要明确文件的路径,并且只能写入自己的沙盒文件夹中。 另外,由于沙盒限制了应用程序对系统的访问权限,某些特定位置的文件写入可能会受到限制。例如,写入`/Applications`文件夹下的文件是不被允许的。因此,需要在写入文件之前,先判断文件路径是否可写,并处理写入失败的情况。 最后,为了确保应用程序在用户重启电脑后仍然能够访问到写入的文件,可以将文件保存在应用程序的特定文件夹中,例如`~/Library/Application Support/YourAppName`。这样,文件将会一直保存在用户的沙盒中,即使应用程序被关闭和重启。 总而言之,要在Mac应用程序的沙盒中写入文件,需要使用特定的API进行操作,并遵循沙盒的限制和规则。这样可以保证应用程序能够安全、可靠地进行文件写入操作,并且在用户重启电脑后仍然能够访问到写入的文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值