在 Android 上从照片 URI 创建文件 [英] Create a file from a photo URI on Android

查看:35
本文介绍了在 Android 上从照片 URI 创建文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 Android 应用,需要让用户从图库中选择一些图片并将这些图片发送到后端(连同一些其他数据).

I have an Android app that needs to let the user select some pictures from the gallery and send these pictures to the backend (together with some other data).

为了允许用户选择我的 Fragment 中有以下图片:

To allow the user to select the pictures I have the following in my Fragment:

private void pickImages() {
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
    startActivityForResult(intent, PICK_PHOTO_FOR_AVATAR);
}

我在这里得到用户选择照片的结果:

I get the result of the selected photos by the user in here:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_PHOTO_FOR_AVATAR && resultCode == Activity.RESULT_OK) {
        if (data == null) {
            //Display an error
            Toast.makeText(getActivity(), "There was an error getting the pictures", Toast.LENGTH_LONG).show();
            return;
        }

        ClipData clipData = data.getClipData();
        String fileName = null, extension = null;

        //if ClipData is null, then we have a regular file
        if (clipData == null) {
            //get the selected file uri
            fileName = FileUtils.getPath(getActivity(), data.getData());
            //obtain the extension of the file
            int index = fileName.lastIndexOf('.');
            if (index > 0) {
                extension = fileName.substring(index + 1);
                if (extension.equals("jpg") || extension.equals("png") || extension.equals("bmp") || extension.equals("jpeg"))
                    isAttachedFile = true;
            }
        }

        ArrayList<Uri> photosUris = new ArrayList<>();

        //for each image in the list of images, add it to the filesUris
        if (clipData != null) for (int i = 0; i < clipData.getItemCount(); i++) {
            ClipData.Item item = clipData.getItemAt(i);
            Uri uri = item.getUri();
            switch (i) {
                case 0:
                    picture1Uri = uri;
                    break;
                case 1:
                    picture2Uri = uri;
                    break;
            }
            photosUris.add(uri);
        }
        else if (isAttachedFile) {
            Uri uri = Uri.parse(fileName);
            picture1Uri = uri;
            photosUris.add(uri);
        }

        uris = photosUris;

        if (picture1Uri != null) {
            image1.setVisibility(View.VISIBLE);
            image1.setImageURI(picture1Uri);
        }
        if (picture2Uri != null) {
            image2.setVisibility(View.VISIBLE);
            image2.setImageURI(picture2Uri);
        }
    }

然后我将 URI 列表发送到演示者,在那里我对后端执行我的 MultiPart Retrofit 调用:

I then send the list of URIs to the Presenter, where I execute my MultiPart Retrofit call to the backend:

//obtain the file(s) information of the message, if any
    if (uris != null && uris.size() > 0) {
        for (int i = 0; i < uris.size(); i++) {
            File file = null;

            //this is the corect way to encode the pictures
            String encodedPath = uris.get(i).getEncodedPath();
            file = new File(encodedPath);

            builder.addFormDataPart("photos[]", file.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file));
        }
    }

    MultipartBody requestBody = builder.build();

    //send the newly generated ticket
    Call<GenerateNewTicketResponse> generateNewTicketCall = OperatorApplication.getApiClient().generateNewTicket(Constants.BEARER + accessToken, requestBody);

问题是这有时有效,有时无效.有时我会收到错误java.io.FileNotFoundException",这会让我陷入 Retrofit 调用的 onFailure() 回调中.

The problem is that this sometimes works, sometimes doesn't. Sometimes I get the error "java.io.FileNotFoundException", which throws me in the onFailure() callback of the Retrofit call.

我发现以下stackoverflow帖子阅读来自 Uri 的文件给出了 java.io.FileNotFoundException: open failed: ENOENT 但我不确定如何针对我的特定情况实施一般建议.

I found the following stackoverflow post Reading File from Uri gives java.io.FileNotFoundException: open failed: ENOENT but I'm not exactly sure how to implement the general suggestion in that response to my particular situation.

获取用户选择的图片的正确路径以便我可以从中创建文件并将它们附加到我的 MultiPart 请求中的正确方法是什么?

What would be the right way to get the right path towards the pictures selected by the user such that I can create files out of them and attach them in my MultiPart request?

Commonsware 建议

Commonsware suggested to

使用 ContentResolver 和 openInputStream() 在 Uri 指向的内容上获取 InputStream.然后,将其传递给您的解码逻辑,例如 BitmapFactory 及其 decodeStream() 方法.

Use a ContentResolver and openInputStream() to get an InputStream on the content pointed to by the Uri. Then, pass that to your decoding logic, such as BitmapFactory and its decodeStream() method.

,但我不确定如何以编程方式做到这一点.

, but I'm not sure exactly how to do that programmatically.

任何帮助将不胜感激.

推荐答案

为了允许用户选择我的 Fragment 中有以下图片:

To allow the user to select the pictures I have the following in my Fragment:

此代码正在使用 ACTION_GET_CONTENT.特别是在 Android 7.0+ 上,通常会返回带有 content 方案的 Uri 值.您的代码假定您使用 file 方案获取 Uri 值,其中路径实际上具有意义.此外,您的代码假定用户正在选择您可以访问的文件系统上的文件,并且没有什么可以强制用户这样做.ACTION_GET_CONTENT 可以被其内容所在的应用支持:

This code is using ACTION_GET_CONTENT. Particularly on Android 7.0+, generally that will return Uri values with a content scheme. Your code assumes that you are getting Uri values with a file scheme, where the path actually has meaning. Moreover, your code assumes that the user is picking files on the filesystem that you can access, and there is nothing that forces the user to do that. ACTION_GET_CONTENT can be supported by apps where their content is:

  • 外部存储上的本地文件
  • 用于其他应用的内部存储本地文件
  • 可移动存储上的本地文件
  • 经过加密且需要即时解密的本地文件
  • 保存在数据库中 BLOB 列中的字节流
  • 互联网上的一段内容,需要其他应用先下载
  • 动态生成的内容
  • ...等等

不要使用 RequestBody.create(),而是使用 此 OkHttp 问题评论.您提供与以前相同的媒体类型,但不是 File(您错误地创建),而是提供一个 ContentResolver(来自 getContentResolver()code> 在 Context) 和 Uri 上.

Instead of using RequestBody.create(), use the InputStreamRequestBody from this OkHttp issue comment. You provide the same media type as before, but instead of a File (that you are incorrectly creating), you provide a ContentResolver (from getContentResolver() on a Context) and the Uri.

这篇博文演示了如何使用 InputStreamRequestBody(特别是原始的 Kotlin 端口)以这种方式上传内容.这篇博文提供了对同一问题的另一种看法,类似的解决方案.

This blog post demonstrates how to use InputStreamRequestBody (specifically a Kotlin port of the original) to upload content in this fashion. This blog post provides another look at the same problem and a similar solution.

这篇关于在 Android 上从照片 URI 创建文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆