Wie liest man mit Jersey mehrere (Datei-) Eingaben mit demselben Namen aus einem mehrteiligen Formular?

Ich habe erfolgreich einen Dienst entwickelt, bei dem ich in Jersey in mehrteiliger Form hochgeladene Dateien lese. Hier ist eine extrem vereinfachte Version von dem, was ich getan habe:

@POST
@Path("FileCollection")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@FormDataParam("file") InputStream uploadedInputStream,
        @FormDataParam("file") FormDataContentDisposition fileDetail) throws IOException {
    //handle the file
}

Dies funktioniert einwandfrei, aber ich habe eine neue Anforderung erhalten. Zusätzlich zu der Datei, die ich hochlade, muss ich eine beliebige Anzahl von Ressourcen verwalten. Nehmen wir an, dies sind Bilddateien.

Ich dachte, ich würde dem Client nur ein Formular mit einer Eingabe für die Datei, einer Eingabe für das erste Bild und einer Schaltfläche bereitstellen, um dem Formular weitere Eingaben hinzuzufügen (mit AJAX oder einfach nur JavaScript).

<form action="blahblahblah" method="post" enctype="multipart/form-data">
   <input type="file" name="file" />
   <input type="file" name="image" />
   <input type="button" value="add another image" />
   <input type="submit"  />
</form>

So kann der Benutzer das Formular mit weiteren Eingaben für Bilder wie folgt anhängen:

<form action="blahblahblah" method="post" enctype="multipart/form-data">
   <input type="file" name="file" />
   <input type="file" name="image" />
   <input type="file" name="image" />
   <input type="file" name="image" />
   <input type="button" value="add another image" />
   <input type="submit"  />
</form>

Ich hoffte, es wäre einfach genug, die Felder mit dem gleichen Namen wie eine Sammlung zu lesen. Ich habe es erfolgreich mit Texteingaben in MVC .NET gemacht und dachte, es wäre nicht schwieriger in Jersey. Es stellte sich heraus, dass ich falsch lag.

Nachdem ich keine Tutorials zu diesem Thema gefunden hatte, begann ich zu experimentieren.

Um zu sehen, wie es geht, habe ich das Problem auf einfache Texteingaben reduziert.

<form action="blahblabhblah" method="post" enctype="multipart/form-data">
   <fieldset>
       <legend>Multiple inputs with the same name</legend>
       <input type="text" name="test" />
       <input type="text" name="test" />
       <input type="text" name="test" />
       <input type="text" name="test" />
       <input type="submit" value="Upload It" />
   </fieldset>
</form>

Offensichtlich brauchte ich eine Art Sammlung als Parameter für meine Methode. Folgendes habe ich versucht, gruppiert nach Sammlungsart.

Array

Zuerst habe ich geprüft, ob Jersey klug genug ist, um mit einem einfachen Array umzugehen:

@POST
@Path("FileCollection")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(@FormDataParam("test") String[] inputs) {
    //handle the request
}

aber das Array wurde nicht wie erwartet injiziert.

MultiValuedMap

Nachdem ich kläglich gescheitert war, erinnerte ich mich daranMultiValuedMap Objekte können sofort bearbeitet werden.

@POST
@Path("FileCollection")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(MultiValuedMap<String, String> formData) {
    //handle the request
}

aber es funktioniert auch nicht. Dieses Mal habe ich eine Ausnahme

SEVERE: A message body reader for Java class javax.ws.rs.core.MultivaluedMap, 
and Java type javax.ws.rs.core.MultivaluedMap<java.lang.String, java.lang.String>, 
and MIME media type multipart/form-data; 
boundary=----WebKitFormBoundaryxgxeXiWk62fcLALU was not found.

Mir wurde gesagt, dass diese Ausnahme beseitigt werden könnte, indem man die einschließtmimepull Deshalb habe ich meinem pom die folgende Abhängigkeit hinzugefügt:

    <dependency>
        <groupId>org.jvnet</groupId>
        <artifactId>mimepull</artifactId>
        <version>1.3</version>
    </dependency>

Leider besteht das Problem weiterhin. Es ist wahrscheinlich eine Frage der Auswahl des richtigen Body-Readers und der Verwendung verschiedener Parameter für das Generikum. Ich bin mir nicht sicher, wie ich das machen soll. Ich möchte sowohl Datei - und Texteingaben als auch einige andere (meistens) verwendenLong Werte und benutzerdefinierte Parameterklassen).

FormDataMultipart

Nach einigen weiteren Nachforschungen fand ich dieFormDataMultiPart Klasse. Ich habe es erfolgreich verwendet, um die Zeichenfolgenwerte aus meinem Formular zu extrahieren

@POST
@Path("upload2")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadMultipart(FormDataMultiPart multiPart){
    List<FormDataBodyPart> fields = multiPart.getFields("test");
    System.out.println("Name\tValue");
    for(FormDataBodyPart field : fields){
        System.out.println(field.getName() + "\t" + field.getValue());
        //handle the values
    }
    //prepare the response
}

Das Problem ist, dies ist eine Lösung für die vereinfachte Version meines Problems. Obwohl ich weiß, dass jeder einzelne von Jersey injizierte Parameter durch das Parsen eines Strings an einem bestimmten Punkt erstellt wird (kein Wunder, es ist schließlich HTTP) und ich einige Erfahrung mit dem Schreiben eigener Parameterklassen habe, kann ich diese Felder nicht wirklich konvertierenInputStream oderFile Instanzen zur Weiterverarbeitung.

Bevor ich in den Jersey-Quellcode eintauche, um zu sehen, wie diese Objekte erstellt werden, habe ich mich entschlossen, hier zu fragen, ob es einen einfacheren Weg gibt, einen Satz (mit unbekannter Größe) von Dateien zu lesen. Wissen Sie, wie Sie dieses Rätsel lösen können?

Antworten auf die Frage(3)

Ihre Antwort auf die Frage