在Android上通过照片URI创建文件

时间:2019-05-25 20:45:11

标签: java android file photo multipart

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

要允许用户选择图片,我的片段中应包含以下内容:

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

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

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

建议的公用软件

  

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

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

任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

  

要允许用户选择图片,我的片段中应包含以下内容:

此代码正在使用ACTION_GET_CONTENT。特别是在Android 7.0+上,通常会使用Uri方案返回content值。您的代码假定您正在通过Uri方案获取file值,其中路径实际上具有含义。而且,您的代码假设用户正在选择您可以访问的文件系统上的文件,并且没有任何强迫用户这样做的事情。 ACTION_GET_CONTENT的内容受以下应用支持:

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

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