Erstellen Sie eine Datei aus einem Foto-URI auf Android

Lesezeit: 5 Minuten

Erstellen Sie eine Datei aus einem Foto URI auf Android
Robert Ruxandrescu

Ich habe eine Android-App, mit der der Benutzer einige Bilder aus der Galerie auswählen und diese Bilder (zusammen mit einigen anderen Daten) an das Backend senden kann.

Damit der Benutzer die Bilder auswählen kann, habe ich Folgendes in meinem 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);
}

Ich bekomme das Ergebnis der ausgewählten Fotos vom Benutzer hier herein:

@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);
        }
    }

Ich sende dann die Liste der URIs an den Presenter, wo ich meinen MultiPart Retrofit-Aufruf an das Backend ausführe:

//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);

Das Problem ist, dass dies manchmal funktioniert, manchmal nicht. Manchmal bekomme ich den Fehler “java.io.FileNotFoundException”, der mich in die onFailure() Rückruf des Retrofit-Aufrufs.

Ich habe den folgenden Stackoverflow-Beitrag gefunden. Lesen der Datei von Uri gibt java.io.FileNotFoundException: Öffnen fehlgeschlagen: ENOENT, aber ich bin mir nicht genau sicher, wie ich den allgemeinen Vorschlag in dieser Antwort auf meine spezielle Situation implementieren soll.

Was wäre der richtige Weg, um den richtigen Weg zu den vom Benutzer ausgewählten Bildern zu finden, damit ich daraus Dateien erstellen und sie in meine MultiPart-Anfrage einfügen kann?

Commonsware vorgeschlagen

Verwenden Sie einen ContentResolver und openInputStream(), um einen InputStream für den Inhalt zu erhalten, auf den der Uri zeigt. Übergeben Sie das dann an Ihre Decodierungslogik, z. B. BitmapFactory und seine Methode decodeStream().

aber ich bin mir nicht sicher, wie ich das programmgesteuert machen soll.

Jede Hilfe wäre willkommen.

Erstellen Sie eine Datei aus einem Foto URI auf Android
CommonsWare

Damit der Benutzer die Bilder auswählen kann, habe ich Folgendes in meinem Fragment:

Dieser Code verwendet ACTION_GET_CONTENT. Besonders auf Android 7.0+ wird das im Allgemeinen zurückkehren Uri Werte mit a content planen. Ihr Code geht davon aus, dass Sie erhalten Uri Werte mit a file Schema, wo der Pfad tatsächlich eine Bedeutung hat. Darüber hinaus geht Ihr Code davon aus, dass der Benutzer Dateien aus dem Dateisystem auswählt, auf die Sie zugreifen können, und es gibt nichts, was den Benutzer dazu zwingt. ACTION_GET_CONTENT können von Apps unterstützt werden, deren Inhalt ist:

  • Eine lokale Datei auf einem externen Speicher
  • Eine lokale Datei im internen Speicher für die andere App
  • Eine lokale Datei auf einem Wechseldatenträger
  • Eine lokale Datei, die verschlüsselt ist und spontan entschlüsselt werden muss
  • Ein Strom von Bytes, die in a gehalten werden BLOB Spalte in einer Datenbank
  • Ein Inhalt im Internet, der zuerst von der anderen App heruntergeladen werden muss
  • Inhalte, die spontan generiert werden
  • …und so weiter

Anstatt zu verwenden RequestBody.create()verwenden Sie die InputStreamRequestBody von dieser OkHttp-Problemkommentar. Sie geben den gleichen Medientyp wie zuvor an, aber anstelle von a File (das Sie fälschlicherweise erstellen), geben Sie a ContentResolver (von getContentResolver() auf einen Context) und das Uri.

Dieser Blogbeitrag demonstriert die Verwendung InputStreamRequestBody (insbesondere eine Kotlin-Portierung des Originals), um Inhalte auf diese Weise hochzuladen. Dieser Blogbeitrag bietet einen weiteren Blick auf dasselbe Problem und eine ähnliche Lösung.

  • Ja, du hast recht. Aber denken Sie daran, dass ich mit meiner Anfrage auch einige andere Schlüssel-Wert-Paare senden muss, nicht nur die Fotos. Ich habe das: MultipartBody.Builder builder = new MultipartBody.Builder(); builder.setType(MultipartBody.FORM); builder.addFormDataPart("details", details); In meiner Retrofit-Oberfläche habe ich folgendes: @POST("ticket") Call<GenerateNewTicketResponse> generateNewTicket( @Header("Authorization") String accessToken, @Body MultipartBody payload); Ich bin mir nicht ganz sicher, wie das mit dem neuen RequestBody zusammenpasst.

    – Robert Ruxandrescu

    25. Mai 2019 um 21:19 Uhr

  • @RobertRuxandrescu: “Ich bin mir nicht ganz sicher, wie das mit dem neuen RequestBody zusammenpasst.” – im Code in Ihrer Frage haben Sie builder.addFormDataPart("photos[]", file.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file));. Das würdest du ersetzen durch builder.addFormDataPart("photos[]", name, new InputStreamRequestBody(MediaType.parse("multipart/form-data"), cr, uris.get(i)));wo cr ist ein ContentResolver. name Sie müssten generieren oder möglicherweise von bekommen DocumentFile.forSingleUri() und getName().

    – CommonsWare

    25. Mai 2019 um 21:24 Uhr


  • @RobertRuxandrescu: Darüber hinaus würde der Rest deines Retrofit-Zeugs unverändert bleiben.

    – CommonsWare

    25. Mai 2019 um 21:25 Uhr

958680cookie-checkErstellen Sie eine Datei aus einem Foto-URI auf Android

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy