java - 在 Android 上从照片 URI 创建文件

标签 java android file photo multipart

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

为了让用户选择我的 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);
}

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

@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 列表发送到 Presenter,我在其中执行对后端的 MultiPart Retrofit 调用:

//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() 回调中。

我找到了以下 stackoverflow 帖子 Reading File from Uri gives java.io.FileNotFoundException: open failed: ENOENT但我不确定如何在针对我的特定情况的响应中实现一般性建议。

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

Commonsware 推荐给

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.

,但我不确定如何以编程方式执行此操作。

如有任何帮助,我们将不胜感激。

最佳答案

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

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

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

不使用 RequestBody.create(),而是使用来自 this OkHttp issue commentInputStreamRequestBody .您提供与以前相同的媒体类型,但您提供的不是 File(您错误地创建),而是 ContentResolver(来自 getContentResolver()Context 上)和 Uri

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

关于java - 在 Android 上从照片 URI 创建文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56308559/

相关文章:

java - 将默认 Java VM 更改为客户端

java - 如何使 activemq 真正可靠?当broker断开连接时消息丢失

android - RelativeLayout ScrollView

Java逐行读取文件并替换第n列

iphone - 在 iphone 中点击按钮时打开文件对话框

php - 使用 curl 上传多个文件

java - Selenium WebDriver "Click"和 JavascriptExecutor Click 有什么区别

java - 在 Java 中初始化 List<List<Integer>>

android - 此机器上未安装 HAXM

android - 通过 NotificationListenerService 读取谷歌地图通知