加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Android开发8.0及以上调用相机/相册,并根据Uri获取图像绝对路径,并进行文件上传

(2023-06-20 20:37:38)
分类: androidios
一、权限问题

    可能会遇到的问题

requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()

Permission Denial: reading com.android.providers.media.MediaProvider

    添加权限

首先在AndroidManifest.xml根节点下添加下面的权限,主要是访问网络、相机、读写权限。

   
   
   
   

    动态请求许可

再andorid新版本里面,上面申请了权限之后,还是需要动态在申请权限,所以再需要用到的界面的onCreate方法里面添加如下代码,进行申请。

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                    requestPermissions(new String[] {Manifest.permission.CAMERA}, 1);
                }
            }
     
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                    requestPermissions(new String[] {Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
                }
            }

二、调用相机

    声明provider

首先,我们需要在主配置文件中声明provider,与activity同级别。之所以要用到provider,是因为从Android7.0开始,就不允许在 App 间,使用 file:// 的方式,传递一个 File ,否则就会抛出异常,而provider的作用恰好就是用过 content://的模式替换掉 file://,看上去只是换了个前缀,但其实是有真实路径转为了虚拟路径。

                    android:authorities="com.example.yourpackage.provider"
                android:name="androidx.core.content.FileProvider"
                android:grantUriPermissions="true">
                                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_paths"/>
           

file_paths的内容

   
                    name = "photo"
            path = "/"/>
   

    调用相机

首先创建一个文件,用于保存拍照图像,然后根据不同系统版本获取Uri,传递给Intent,然后调起相机(可以考虑将outputImage、imageUri设置为全局变量)。

    int REQUEST_CODE = 1;  //事件请求CODE为1
    outputImage = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES),"last.jpg");
    if (outputImage.exists())
        outputImage.delete();
        try {
            outputImage.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    //注意com.example.yourpackage.provider要和provider声明中的一致
    imageUri = (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N) ? FileProvider.getUriForFile(context,"com.example.yourpackage.provider",outputImage) : Uri.fromFile(outputImage);
    Intent intent1 = new Intent("android.media.action.IMAGE_CAPTURE");
    intent1.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    startActivityForResult(intent1,REQUEST_CODE);

    处理回调

使用BitmapFactory读取imageUri,得到bitmap,然后进行一些压缩,然后显示。

    if (resultCode == Activity.RESULT_OK) {
        ContentResolver contentResolver = getContentResolver();
        Bitmap bitmap = null;
        try {
            bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(imageUri));
            Log.i("TAG", "从相册回传bitmap:"+bitmap.getWidth());
            Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap,  bitmap.getWidth()/10 ,bitmap.getHeight()/10, true);
            img_imgview.setImageBitmap(bitmap2);
            this.flag = 1;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    获取图像绝对路径

我们使用outputImage,来获取绝对路径,用于上传或者其它操作。

outputImage.getAbsolutePath()

三、调用相册

    调用相册

    nt REQUEST_CODE = 2;
    Intent intent1 = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent1.addCategory(Intent.CATEGORY_OPENABLE);
    intent1.setType("image
        public static String getFileAbsolutePath(Context context, Uri imageUri) {
            if (context == null || imageUri == null) {
                return null;
            }
     
            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
                return getRealFilePath(context, imageUri);
            }
     
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && DocumentsContract.isDocumentUri(context, imageUri)) {
                if (isExternalStorageDocument(imageUri)) {
                    String docId = DocumentsContract.getDocumentId(imageUri);
                    String[] split = docId.split(":");
                    String type = split[0];
                    if ("primary".equalsIgnoreCase(type)) {
                        return Environment.getExternalStorageDirectory() + "/" + split[1];
                    }
                } else if (isDownloadsDocument(imageUri)) {
                    String id = DocumentsContract.getDocumentId(imageUri);
                    Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
                    return getDataColumn(context, contentUri, null, null);
                } else if (isMediaDocument(imageUri)) {
                    String docId = DocumentsContract.getDocumentId(imageUri);
                    String[] split = docId.split(":");
                    String type = split[0];
                    Uri contentUri = null;
                    if ("image".equals(type)) {
                        contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                    } else if ("video".equals(type)) {
                        contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                    } else if ("audio".equals(type)) {
                        contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                    }
                    String selection = MediaStore.Images.Media._ID + "=?";
                    String[] selectionArgs = new String[]{split[1]};
                    return getDataColumn(context, contentUri, selection, selectionArgs);
                }
            } // MediaStore (and general)
            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
                return uriToFileApiQ(context,imageUri);
            }
            else if ("content".equalsIgnoreCase(imageUri.getScheme())) {
                // Return the remote address
                if (isGooglePhotosUri(imageUri)) {
                    return imageUri.getLastPathSegment();
                }
                return getDataColumn(context, imageUri, null, null);
            }
            // File
            else if ("file".equalsIgnoreCase(imageUri.getScheme())) {
                return imageUri.getPath();
            }
            return null;
        }
     
        //此方法 只能用于4.4以下的版本
        private static String getRealFilePath(final Context context, final Uri uri) {
            if (null == uri) {
                return null;
            }
            final String scheme = uri.getScheme();
            String data = null;
            if (scheme == null) {
                data = uri.getPath();
            } else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
                data = uri.getPath();
            } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
                String[] projection = {MediaStore.Images.ImageColumns.DATA};
                Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
     
    //            Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
                if (null != cursor) {
                    if (cursor.moveToFirst()) {
                        int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                        if (index > -1) {
                            data = cursor.getString(index);
                        }
                    }
                    cursor.close();
                }
            }
            return data;
        }
     
     
       
        private static boolean isExternalStorageDocument(Uri uri) {
            return "com.android.externalstorage.documents".equals(uri.getAuthority());
        }
     
       
        private static boolean isDownloadsDocument(Uri uri) {
            return "com.android.providers.downloads.documents".equals(uri.getAuthority());
        }
     
        private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
            Cursor cursor = null;
            String column = MediaStore.Images.Media.DATA;
            String[] projection = {column};
            try {
                cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
                if (cursor != null && cursor.moveToFirst()) {
                    int index = cursor.getColumnIndexOrThrow(column);
                    return cursor.getString(index);
                }
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
            return null;
        }
     
       
        private static boolean isMediaDocument(Uri uri) {
            return "com.android.providers.media.documents".equals(uri.getAuthority());
        }
     
       
        private static boolean isGooglePhotosUri(Uri uri) {
            return "com.google.android.apps.photos.content".equals(uri.getAuthority());
        }
     
     
       
        public static String getFileFromContentUri(Context context, Uri uri) {
            if (uri == null) {
                return null;
            }
            String filePath;
            String[] filePathColumn = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME};
            ContentResolver contentResolver = context.getContentResolver();
            Cursor cursor = contentResolver.query(uri, filePathColumn, null,
                    null, null);
            if (cursor != null) {
                cursor.moveToFirst();
                try {
                    filePath = cursor.getString(cursor.getColumnIndex(filePathColumn[0]));
                    return filePath;
                } catch (Exception e) {
                } finally {
                    cursor.close();
                }
            }
            return "";
        }
     
       
        @RequiresApi(api = Build.VERSION_CODES.Q)
        private static String uriToFileApiQ(Context context, Uri uri) {
            File file = null;
            //android10以上转换
            if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
                file = new File(uri.getPath());
            } else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
                //把文件复制到沙盒目录
                ContentResolver contentResolver = context.getContentResolver();
                Cursor cursor = contentResolver.query(uri, null, null, null, null);
                if (cursor.moveToFirst()) {
                    String displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
                    try {
                        InputStream is = contentResolver.openInputStream(uri);
                        File cache = new File(context.getExternalCacheDir().getAbsolutePath(), Math.round((Math.random() + 1) * 1000) + displayName);
                        FileOutputStream fos = new FileOutputStream(cache);
                        FileUtils.copy(is, fos);
                        file = cache;
                        fos.close();
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return file.getAbsolutePath();
        }
    }

五、http请求并上传

    通过okhttp调用接口

修改模块的build.gradle 增加下面一行依赖配置

implementation 'com.squareup.okhttp3:okhttp:4.9.1

    Get请求

    OkHttpClient client = new OkHttpClient(); // 创建一个okhttp客户端对象
    // 创建一个GET方式的请求结构
    xRequest request = new Request.Builder()
        //.get() // 因为OkHttp默认采用get方式,所以这里可以不调get方法
        .header("Accept-Language", "zh-CN") // 给http请求添加头部信息
        .url("http://192.168.1.104:5291/myApi/GetList") // 指定http请求的调用地址
        .build();
    Call call = client.newCall(request); // 根据请求结构创建调用对象
    // 加入HTTP请求队列。异步调用,并设置接口应答的回调方法
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) { // 请求失败
            // 回到主线程操纵界面
            runOnUiThread(() -> {
                Toast toast= Toast.makeText(context, "默认失败的Toast" + e.getMessage().toString(), Toast.LENGTH_SHORT);
                toast.show();
            });
        }
     
        @Override
        public void onResponse(Call call, final Response response) throws IOException { // 请求成功
            String resp = response.body().string();
            // 回到主线程操纵界面
            runOnUiThread(() -> {
                Toast toast= Toast.makeText(context, "默认成功的的Toast" + resp, Toast.LENGTH_SHORT);
                toast.show();
            });
        }
    });

    Post from表单上传文件和数据

    //文件
    File file = new File(picUrl);
    //请求体
    MultipartBody.Builder builder = new MultipartBody.Builder();
    builder.setType(MultipartBody.FORM);
    //数据1
    builder.addFormDataPart("date", date);
    //数据2
    builder.addFormDataPart("banzu", banzu);
    //文件,注意名称,这里是files,后台需要用这个名字接数据
    builder.addFormDataPart("files", file.getName(), RequestBody.create(MediaType.parse("image/jpeg"), file));
    MultipartBody body = builder.build();
     
    // 创建一个okhttp客户端对象,设置超时
    OkHttpClient client = new OkHttpClient().newBuilder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS).build();
     
    // 创建一个POST方式的请求结构
    Request request = new Request.Builder().post(body).url("http://192.168.1.100:5290/upLoadFile").build();
    Call call = client.newCall(request); // 根据请求结构创建调用对象
    // 加入HTTP请求队列。异步调用,并设置接口应答的回调方法
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) { // 请求失败
            // 回到主线程操纵界面
            runOnUiThread(() -> {
                Toast toast= Toast.makeText(context, "默认失败的Toast" + e.getMessage().toString(), Toast.LENGTH_SHORT);
                toast.show();
                //关闭loading
                dialog.dismiss();
            });
        }
     
        @Override
        public void onResponse(Call call, final Response response) throws IOException { // 请求成功
            String resp = response.body().string();
            // 回到主线程操纵界面
            runOnUiThread(() -> {
                Toast toast= Toast.makeText(context, "默认成功的Toast", Toast.LENGTH_SHORT);
                toast.show();
                //关闭loading
                dialog.dismiss();
            });
        }
    });

    Post Json数据

    private void postJson() {
            String username = et_username.getText().toString();
            String password = et_password.getText().toString();
            String jsonString = "";
            try {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("username", username);
                jsonObject.put("password", password);
                jsonString = jsonObject.toString();
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 创建一个POST方式的请求结构
            RequestBody body = RequestBody.create(jsonString, MediaType.parse("text/plain;charset=utf-8"));
            OkHttpClient client = new OkHttpClient(); // 创建一个okhttp客户端对象
            Request request = new Request.Builder().post(body).url(URL_LOGIN).build();
            Call call = client.newCall(request); // 根据请求结构创建调用对象
            // 加入HTTP请求队列。异步调用,并设置接口应答的回调方法
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) { // 请求失败
                    // 回到主线程操纵界面
                    runOnUiThread(() -> tv_result.setText("调用登录接口报错:"+e.getMessage()));
                }
     
                @Override
                public void onResponse(Call call, final Response response) throws IOException { // 请求成功.setText("调用登录接口返回:\n"+resp));
                }
            });
        }

六、参考代码下载

https://download.csdn.net/download/bashendixie5/87823865
————————————————
版权声明:本文为CSDN博主「坐望云起」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/bashendixie5/article/details/129745348

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有