web-dev-qa-db-fra.com

@RequestPart avec demande mixte en plusieurs parties, Spring MVC 3.2

Je développe un service RESTful basé sur Spring 3.2. Je suis confronté à un problème avec un contrôleur gérant une requête HTTP multipartie mixte, avec une deuxième partie avec des données formatées XMLor JSON et une deuxième partie avec un fichier image.

J'utilise @ annotation @ RequestPart pour recevoir la demande

@RequestMapping(value = "/User/Image", method = RequestMethod.POST,  consumes = {"multipart/mixed"},produces="applcation/json")

public
ResponseEntity<List<Map<String, String>>> createUser(
        @RequestPart("file") MultipartFile file, @RequestPart(required=false) User user) {

    System.out.println("file" + file);

    System.out.println("user " + user);

    System.out.println("received file with original filename: "
            + file.getOriginalFilename());

    // List<MultipartFile> files = uploadForm.getFiles();
    List<Map<String, String>> response = new ArrayList<Map<String, String>>();
    Map<String, String> responseMap = new HashMap<String, String>();

    List<String> fileNames = new ArrayList<String>();

    if (null != file) {
        // for (MultipartFile multipartFile : files) {

        String fileName = file.getOriginalFilename();
        fileNames.add(fileName);

        try {
            file.transferTo(new File("C:/" + file.getOriginalFilename()));
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    responseMap.put("displayText", file.getOriginalFilename());
    responseMap.put("fileSize", "" + file.getSize());
    response.add(responseMap);

    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.add("Accept", "application/json");
    return new ResponseEntity<List<Map<String, String>>>(response,
            httpHeaders, HttpStatus.OK);

}

User.Java sera comme ça-

@XmlRootElement(name = "User")


public class User implements Serializable { 
    private static final long serialVersionUID = 1L;

    private int userId;
    private String name;
    private String email;

    private String company;
    private String gender;

    //getter setter of the data members
}

À ma connaissance, en utilisant l'annotation @RequestPart, je m'attends à ce que la section multipartie XML soit évaluée en fonction de son type de contenu et finalement non marshallée dans ma classe utilisateur (j'utilise Jaxb2, le marshaller/unmarhaller est correctement configuré dans le contexte d'application et la procédure fonctionne correctement pour toutes les autres méthodes de contrôleur lorsque je passe les données XML en tant que corps et utilise l'annotation @RequestBody).

Mais ce qui se passe réellement, c'est que, bien que le fichier soit correctement trouvé et analysé en tant que MultipartFile, la partie "utilisateur" n'est jamais vue et la demande échoue toujours, ne correspondant pas à la signature de la méthode du contrôleur.

J'ai reproduit le problème avec plusieurs types de clients et je suis convaincu que le format de la demande en plusieurs parties est correct.

S'il vous plaît, aidez-moi à résoudre ce problème.Une solution de contournement sera peut-être là pour recevoir une demande mixte/multipartite.

Merci et salutations,

Raghvendra

16
Raghvendra

J'ai réussi à résoudre le problème

Exemple de point final:

@PostMapping("/")
public Document create(@RequestPart Document document,
                       @RequestPart(required = false) MultipartFile file) {
    log.debug("#create: document({}), file({})", delegation, file);
    //custom logic
    return document;
}

Exception:

"error_message": "Content type 'application/octet-stream' not supported"

Une exception est levée de la méthode suivante:

org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(HttpInputMessage,MethodParameter,Type)

Solution:

Nous devons créer un convertisseur personnalisé @Component, qui implémente HttpMessageConverter ou HttpMessageConverter et connaît MediaType.APPLICATION_OCTET_STREAM . Pour une solution de contournement simple, il suffit d'étendre AbstractJackson2HttpMessageConverter

@Component
public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {

/**
 * Converter for support http request with header Content-Type: multipart/form-data
 */
public MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) {
    super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
}

@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
    return false;
}

@Override
public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
    return false;
}

@Override
protected boolean canWrite(MediaType mediaType) {
    return false;
}
}
14
Maksim

Je ne sais pas si vous avez résolu votre problème, mais j'ai également eu un problème similaire où mon objet JSON n'était pas récupéré par mon contrôleur lors du mélange de @RequestPart et MultipartFile.

La signature de la méthode pour votre appel semble correcte:

public ResponseEntity<List<Map<String, String>>> createUser(
        @RequestPart("file") MultipartFile file, @RequestPart(required=false) User user) {

// ... CODE ... 
}

Cependant, assurez-vous que votre demande ressemble à ceci:

POST /createUser
Content-Type: multipart/mixed; boundary=B0EC8D07-EBF1-4EA7-966C-E492A9F2C36E

--B0EC8D07-EBF1-4EA7-966C-E492A9F2C36E
Content-Disposition: form-data; name="user";
Content-Type: application/xml; charset=UTF-8

<user><!-- your user xml --></user>
--B0EC8D07-EBF1-4EA7-966C-E492A9F2C36E
Content-Disposition: form-data; name="file"; filename="A551A700-46D4-470A-86E7-52AD2B445847.dat"
Content-Type: application/octet-stream

/// FILE DATA
--B0EC8D07-EBF1-4EA7-966C-E492A9F2C36E--
9

J'ai réussi à résoudre le problème:

    @SuppressWarnings("rawtypes")
@RequestMapping(value = "/DataTransfer", method = RequestMethod.POST, produces = {
        MediaType.APPLICATION_JSON_UTF8_VALUE }, consumes = {  MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE} )
@ApiOperation(value = "Sbm Data Transfer Service", response = Iterable.class)
@ApiResponses(value = { @ApiResponse(code = 200, message = "Successfully find."),
        @ApiResponse(code = 400, message = "There has been an error."),
        @ApiResponse(code = 401, message = "You are not authorized to save the resource"),
        @ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
        @ApiResponse(code = 404, message = "The resource you were trying to reach is not found") })
ResponseEntity processDataTransfer(@RequestPart(name="file") MultipartFile  file, @RequestPart(name="param") DataTransferInputDto param);
0
Erkan Köseoğlu

Vous pouvez utiliser @RequestPart depuis org.springframework.web.bind.annotation.RequestPart; Il est utilisé comme combinaison de @RequestBody et de téléchargement de fichiers.

En utilisant @RequestParam comme ceci @RequestParam ("file") Fichier multipartFile, vous pouvez télécharger uniquement un fichier et plusieurs données uniques (valeur clé) comme

    @RequestMapping(value = "/uploadFile", method = RequestMethod.POST,  consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
    public void saveFile(
                         @RequestParam("userid") String userid,
                         @RequestParam("file") MultipartFile file) {

    }

vous pouvez publier des données d'objet JSON et un fichier en utilisant @RequestPart comme

    @RequestMapping(value = "/patientp", method = RequestMethod.POST,  consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<?> insertPatientInfo(
                                            @RequestPart PatientInfoDTO patientInfoDTO,
                                            @RequestPart("file") MultipartFile file) {
}

Vous n'êtes pas limité à l'utilisation de téléchargements de fichiers en plusieurs parties directement comme paramètres de méthode du contrôleur. Vos objets de formulaire peuvent contenir des champs Part ou MultipartFile, et Spring sait automatiquement qu'il doit obtenir les valeurs des parties de fichier et convertit les valeurs de manière appropriée.

La méthode ci-dessus peut répondre à la demande en plusieurs parties précédemment démontrée contenant un seul fichier. Cela fonctionne car Spring dispose d'un convertisseur de messages HTTP intégré qui reconnaît les parties de fichier. En plus du type javax.servlet.http.Part, vous pouvez également convertir les téléchargements de fichiers en org.springframework.web.multipart.MultipartFile. Si le champ de fichier permet plusieurs téléchargements de fichiers, comme illustré dans la deuxième demande en plusieurs parties, utilisez simplement un tableau ou une collection de pièces ou des fichiers en plusieurs parties.

        @RequestMapping(value = "/patientp", method = RequestMethod.POST,  consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
    public ResponseEntity<?> insertPatientInfo(
                                                @RequestPart PatientInfoDTO patientInfoDTO,
                                                @RequestPart("files") List<MultipartFile> files) {
    }

Heureux d'aider...

0
Pramod Wayabase