web-dev-qa-db-fra.com

Fichier chargé avec un autre objet dans le service Web reposant de Jersey

Je souhaite créer des informations sur les employés dans le système en téléchargeant une image avec les données des employés. Je suis capable de le faire avec différents appels de repos en utilisant un maillot. Mais je veux réaliser en un seul appel… .. Je fournis ci-dessous la structure. S'il vous plaît aidez-moi comment faire à cet égard.

@POST
@Path("/upload2")
@Consumes({MediaType.MULTIPART_FORM_DATA,MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response uploadFileWithData(
        @FormDataParam("file") InputStream fileInputStream,
        @FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
        Employee emp) {

//..... business login

}

Chaque fois que j'essaie de le faire, je reçois une erreur dans Chrome postman. La structure simple de mon employé json est donnée ci-dessous.

{
    "Name": "John",
    "Age": 23,
    "Email": "[email protected]",
    "Adrs": {
        "DoorNo": "12-A",
        "Street": "Street-11",
        "City": "Bangalore",
        "Country": "Karnataka"
    }
}

Cependant, je peux le faire en effectuant deux appels différents, mais je souhaite obtenir un appel en repos pour pouvoir recevoir le fichier ainsi que les données réelles de l'employé.

Vous demander de l'aide à cet égard.

48
Sambit

Vous ne pouvez pas avoir deux Content-Types (bien techniquement c'est ce que nous faisons ci-dessous, mais ils sont séparés avec chaque partie du multipart, mais le type principal est multipart). C'est essentiellement ce que vous attendez de votre méthode. Vous attendez mutlipart et json ensemble comme type de support principal. Les données Employee doivent faire partie du multipart. Vous pouvez donc ajouter un @FormDataParam("emp") pour la Employee.

@FormDataParam("emp") Employee emp) { ...

Voici la classe que j'ai utilisée pour tester

@Path("/multipart")
public class MultipartResource {

    @POST
    @Path("/upload2")
    @Consumes({MediaType.MULTIPART_FORM_DATA})
    public Response uploadFileWithData(
            @FormDataParam("file") InputStream fileInputStream,
            @FormDataParam("file") FormDataContentDisposition cdh,
            @FormDataParam("emp") Employee emp) throws Exception{

        Image img = ImageIO.read(fileInputStream);
        JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(img)));
        System.out.println(cdh.getName());
        System.out.println(emp);

        return Response.ok("Cool Tools!").build();
    } 
}

Tout d'abord, je viens de tester avec l'API client pour m'assurer que cela fonctionne

@Test
public void testGetIt() throws Exception {

    final Client client = ClientBuilder.newBuilder()
        .register(MultiPartFeature.class)
        .build();
    WebTarget t = client.target(Main.BASE_URI).path("multipart").path("upload2");

    FileDataBodyPart filePart = new FileDataBodyPart("file", 
                                             new File("stackoverflow.png"));
    // UPDATE: just tested again, and the below code is not needed.
    // It's redundant. Using the FileDataBodyPart already sets the
    // Content-Disposition information
    filePart.setContentDisposition(
            FormDataContentDisposition.name("file")
                                    .fileName("stackoverflow.png").build());

    String empPartJson
            = "{"
            + "  \"id\": 1234,"
            + "  \"name\": \"Peeskillet\""
            + "}";

    MultiPart multipartEntity = new FormDataMultiPart()
            .field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
            .bodyPart(filePart);

    Response response = t.request().post(
            Entity.entity(multipartEntity, multipartEntity.getMediaType()));
    System.out.println(response.getStatus());
    System.out.println(response.readEntity(String.class));

    response.close();
}

Je viens de créer une simple classe Employee avec un champ id et name pour les tests. Cela fonctionne parfaitement bien. Il affiche l'image, imprime la disposition du contenu et imprime l'objet Employee.

Je ne connais pas trop Postman, alors j’ai enregistré ces tests pour la dernière fois :-)

enter image description here

Cela semble également fonctionner correctement, comme vous pouvez le constater avec la réponse "Cool Tools". Mais si nous regardons les données Employee imprimées, nous verrons qu'elles sont nulles. Ce qui est bizarre, car avec l’API client cela fonctionnait bien.

Si nous regardons la fenêtre d'aperçu, nous verrons le problème

enter image description here

Il n'y a pas d'en-tête Content-Type pour la partie de corps emp. Vous pouvez voir dans l'API client que je l'ai explicitement définie

MultiPart multipartEntity = new FormDataMultiPart()
        .field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
        .bodyPart(filePart);

Donc, je suppose que ce n’est vraiment que une partie d’une réponse complète. Comme je l'ai dit, je ne connais pas bien Postman. Je ne sais donc pas comment définir Content-Types pour des parties individuelles du corps. Le image/png pour l'image a été automatiquement défini pour moi pour la partie image (je suppose que cela vient d'être déterminé par l'extension du fichier). Si vous pouvez comprendre cela, alors le problème devrait être résolu. S'il vous plaît, si vous savez comment faire cela, postez-le comme une réponse.


Et juste pour être complet ...

Configurations de base:

Dépendance:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-multipart</artifactId>
    <version>${jersey2.version}</version>
</dependency>

Configuration client:

final Client client = ClientBuilder.newBuilder()
    .register(MultiPartFeature.class)
    .build();

Configuration du serveur:

// Create JAX-RS application.
final Application application = new ResourceConfig()
    .packages("org.glassfish.jersey.examples.multipart")
    .register(MultiPartFeature.class);

METTRE À JOUR

Ainsi, comme vous pouvez le constater à partir du client Postman, certains clients ne peuvent pas définir le type de contenu de chaque composant, y compris le navigateur, en ce qui concerne ses fonctionnalités par défaut avec FormData (js).

Nous ne pouvons pas nous attendre à ce que le client découvre la situation, alors ce que nous pouvons faire, c’est lors de la réception des données, définir explicitement le type de contenu avant la désérialisation. Par exemple

@POST
@Path("upload2")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(@FormDataParam("emp") FormDataBodyPart jsonPart,
                                  @FormDataParam("file") FormDataBodyPart bodyPart) { 
     jsonPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
     Employee emp = jsonPart.getValueAs(Employee.class);
}

Obtenir un POJO représente un peu plus de travail, mais c'est une meilleure solution que d'obliger le client à essayer de trouver sa propre solution.


À part

  • Il existe une conversation dans ces commentaires qui pourrait vous intéresser si vous utilisez un connecteur différent de celui par défaut HttpUrlConnection. 
81
Paul Samsotha

Vous pouvez accéder au fichier image et aux données d'un formulaire à l'aide de MULTIPART FORM DATA en utilisant le code ci-dessous.

@POST
@Path("/UpdateProfile")
@Consumes(value={MediaType.APPLICATION_JSON,MediaType.MULTIPART_FORM_DATA})
@Produces(value={MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response updateProfile(
    @FormDataParam("file") InputStream fileInputStream,
    @FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
    @FormDataParam("ProfileInfo") String ProfileInfo,
    @FormDataParam("registrationId") String registrationId) {

    String filePath= "/filepath/"+contentDispositionHeader.getFileName();

    OutputStream outputStream = null;
    try {
        int read = 0;
        byte[] bytes = new byte[1024];
        outputStream = new FileOutputStream(new File(filePath));

        while ((read = fileInputStream.read(bytes)) != -1) {
            outputStream.write(bytes, 0, read);
        }

        outputStream.flush();
        outputStream.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (outputStream != null) { 
            try {
                outputStream.close();
            } catch(Exception ex) {}
        }
    }
}
1
zameer

Je veux ajouter un commentaire sur peeskillet mais je n'ai pas 50 points de réputation, donc j'ajoute comme réponse:

Lorsque j'ai essayé la solution @peeskillet avec le client Jersey 2.21.1, il y avait une erreur 400. Cela a fonctionné quand j'ai ajouté ce qui suit dans mon code client:

  MediaType contentType = MediaType.MULTIPART_FORM_DATA_TYPE;
  contentType = Boundary.addBoundary(contentType);

  Response response = t.request().post(
        Entity.entity(multipartEntity, contentType));

au lieu de MediaType.MULTIPART_FORM_DATA codé en dur dans l’appel post-demande.

0
mkag

Votre ApplicationConfig doit enregistrer MultiPartFeature.class à partir de glassfish.jersey.media .. afin de permettre le téléchargement de fichier.

@javax.ws.rs.ApplicationPath(ResourcePath.API_ROOT)
public class ApplicationConfig extends ResourceConfig {  
public ApplicationConfig() {
        //register the necessary headers files needed from client
        register(CORSConfigurationFilter.class);
        //The jackson feature and provider is used for object serialization
        //between client and server objects in to a json
        register(JacksonFeature.class);
        register(JacksonProvider.class);
        //Glassfish multipart file uploader feature
        register(MultiPartFeature.class);
        //inject and registered all resources class using the package
        //not to be tempered with
        packages("com.flexisaf.safhrms.client.resources");
        register(RESTRequestFilter.class);
    }
0
pitaside