web-dev-qa-db-fra.com

Comment traiter une demande en plusieurs parties composée d'un fichier et d'un objet JSON dans Spring Restful Service?

J'ai la ressource suivante (implémentée à l'aide de Spring 4.05.RELEASE) qui accepte un fichier et un objet JSON:

(P.S. activityTemplate est une classe d'entité sérialisable)

...
@RequestMapping(value="/create", method=RequestMethod.POST)
public @ResponseBody ActivityTemplate createActivityTemplate(
        @RequestPart ActivityTemplate activityTemplate, @RequestPart MultipartFile jarFile)
{
   //process the file and JSON
}
...

et voici le formulaire que je teste:

<form method="POST" enctype="multipart/form-data"
    action="http://localhost:8080/activityTemplates/create">
    JSON: <input type="text" name="activityTemplate" value='/* the JSON object*/'><br />

    File to upload: <input type="file" name="file">
    <input type="submit" value="Upload">
</form>

et voici l'erreur que j'obtiens:

 There was an unexpected error (type=Unsupported Media Type, status=415).
 Content type 'application/octet-stream' not supported

Alors, comment dois-je faire en sorte que la ressource accepte l'objet JSON dans le cadre de la demande en plusieurs parties, ou dois-je envoyer le formulaire d'une manière différente?

19
Sami

Cela m'a pris deux jours pour travailler pour moi!

client (angulaire):

$scope.saveForm = function () {
      var formData = new FormData();
      var file = $scope.myFile;
      var json = $scope.myJson;
      formData.append("file", file);
      formData.append("ad",JSON.stringify(json));//important: convert to string JSON!
      var req = {
        url: '/upload',
        method: 'POST',
        headers: {'Content-Type': undefined},
        data: formData,
        transformRequest: function (data, headersGetterFunction) {
          return data;
        }
      };

Printemps (démarrage):

@RequestMapping(value = "/upload", method = RequestMethod.POST)
    public @ResponseBody
    Advertisement storeAd(@RequestPart("ad") String adString, @RequestPart("file") MultipartFile file) throws IOException {

        Advertisement jsonAd = new ObjectMapper().readValue(adString, Advertisement.class);
//do whatever you want with your file and jsonAd
25
mohi

J'espère que cela devrait vous aider. Vous devez définir la limite dans votre demande pour informer la demande HTTP. est simple; Une brève introduction au format multipart se trouve dans le lien ci-dessous

Spécification HTML 4.01 pour les parties multiples

L'exemple suivant illustre le codage " multipart/form-data ". Si l'objet Json est " MyJsonObj ", et le fichier qui doit be send is " myfile.txt ", l'agent utilisateur peut renvoyer les données suivantes:

Content-Type: multipart/form-data; boundary=MyBoundary

--MyBoundary
Content-Disposition: form-data; name="myJsonString"
Content-Type: application/json

MyJsonObj //Your Json Object goes here
--MyBoundary
Content-Disposition: form-data; name="files"; filename="myfile.txt"
Content-Type: text/plain

... contents of myfile.txt ...
--MyBoundary--

ou si vos fichiers sont de type image avec le nom " image.gif " alors,

--MyBoundary
Content-Disposition: file; filename="image.gif"
Content-Type: image/gif
Content-Transfer-Encoding: binary

...contents of image.gif...
--MyBoundary--

Vous spécifiez boundary dans le Content-Type header pour que le serveur sache diviser les données envoyées.

Donc, vous devez essentiellement sélectionner une valeur limite pour:

  • Utilisez une valeur qui n'apparaîtra pas dans les données HTTP envoyées au serveur comme 'AaB03x'.
  • Soyez cohérent et utilisez la même valeur dans toute la demande.
2
Sridhar DD

Vous n'avez pas donné les noms des paramètres à vos @ RequestPart s?

public @ResponseBody ActivityTemplate createActivityTemplate(
    @RequestPart("activityTemplate") ActivityTemplate activityTemplate, @RequestPart("file") MultipartFile jarFile)
{
   //process the file and JSON
}

Remarque: n'oubliez pas d'inclure le mappeur jackson .jar ( mappe votre fichier Json vers ActivityTemplate ) dans votre chemin de classe.

1
codeMan

Ne pourriez-vous pas changer votre

@RequestMapping(value="/create", method=RequestMethod.POST)

à

@RequestMapping(value="/create",
                method=RequestMethod.POST, consumes ={"multipart/form-data"})
1
Vogel

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 de 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 autorise 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

Une exception est levée car vous n'avez pas le HttpMessageConverter approprié pour traiter la demande de données en plusieurs parties/formulaire. Solution

0
Maksim

cela peut vous aider, lors de la réception de MultipartFile, vous devez définir le type de contenu de l'en-tête de la demande sur "multipart/form-data", puis dans votre contrôleur, utilisez consumes = "multipart/form-data", consomme également utilisé pour mapper notre demande à notre méthode. dans le contrôleur.

Si vous souhaitez recevoir des données JSON, mieux vaut envoyer une demande sous forme de JSONString, il vous suffit de recevoir cette jsonstring, puis de la convertir au format json Object, puis d'utiliser cet objet pour vos opérations.

vérifier le code ci-dessous:

@RequestMapping(value="/savingImg", method=RequestMethod.POST, 
        consumes="multipart/form-data", produces="application/json")
public ResponseEntity<?> savingAppIMgDtlss(
        @RequestParam(value="f1", required = false) MultipartFile f1 , 
        @RequestParam(value="f2", required = false) MultipartFile f2 ,
        @RequestParam(value="f3", required = false) MultipartFile f3 ,
        @RequestParam(value="f4", required = false) MultipartFile f4 ,
        @RequestParam(value="f5", required = false) MultipartFile f5 ,
        @RequestParam(value="f6", required = false) MultipartFile f6 ,
        @RequestParam(value="f7", required = false) MultipartFile f7 ,
        @RequestParam(value="f8", required = false) MultipartFile f8 ,@RequestParam("data") String jsonString) 
                throws Exception , ParseException {
    try{
        JSONObject gstcTranObj = new JSONObject();
                //converting JSONString to JSON
        net.sf.json.JSONObject jsonDtls = net.sf.json.JSONObject.fromObject(jsonString);
        System.out.println("f1::"+f1.getOriginalFilename());
        System.out.println("f2::"+f2.getOriginalFilename());
        System.out.println("f3::"+f3.getOriginalFilename());
        System.out.println("f4::"+f4.getOriginalFilename());
        System.out.println("f5::"+f5.getOriginalFilename());
        System.out.println("f6::"+f6.getOriginalFilename());
        System.out.println("f7::"+f7.getOriginalFilename());
        System.out.println("f8::"+f8.getOriginalFilename());
} catch (Exception e) {
        e.printStackTrace();

        return new ResponseEntity<>("Failed",HttpStatus.NOT_FOUND);
    }finally{

    }
return new ResponseEntity<>("Success", HttpStatus.OK);

  }
}
0

Le message d'erreur indique qu'aucun HttpMessageConverter n'est enregistré pour une partie en plusieurs parties/MIME de type de contenu: application/octet-stream. Pourtant, votre paramètre jarFile est très probablement correctement identifié comme application/octet-stream, donc je suppose qu'il y a un décalage dans le mappage des paramètres.

Donc, essayez d'abord de définir le même nom pour le paramètre et l'élément d'entrée du formulaire.

Un autre problème est que le JSON est téléchargé en tant que valeur (régulière) d'une entrée de texte dans le formulaire, et non en tant que partie distincte dans le MIME en plusieurs parties. Il n'y a donc pas d'en-tête de type de contenu associé pour savoir que Spring doit utiliser le désérialiseur JSON. Vous pouvez utiliser @RequestParam à la place et enregistrez un convertisseur spécifique comme dans cette réponse: paramètre JSON dans le contrôleur MVC à ressort

0
miw

Le type de contenu par défaut est "application/octet-stream". Puisque vous téléchargez un fichier jar et JSON, le type de contenu doit être défini dans le @RequestMapping annotation comme suit:

@RequestMapping(value="/create", method=RequestMethod.POST, headers="content-type=application/json,application/Java-archive")
0
Mithun