Comment télécharger un fichier avec des métadonnées à l’aide d’un service Web REST?

J’ai un service Web REST qui expose actuellement cette URL:

http: // serveur / data / media

où les utilisateurs peuvent POST le JSON suivant:

 { "Name": "Test", "Latitude": 12.59817, "Longitude": 52.12873 } 

afin de créer une nouvelle métadonnée multimédia.

Maintenant, j’ai besoin de la possibilité de télécharger un fichier en même temps que les métadonnées des médias. Quelle est la meilleure façon de procéder? Je pourrais introduire une nouvelle propriété appelée file et encoder le fichier base64, mais je me demandais s’il y avait une meilleure façon.

Il y a aussi l’utilisation de multipart/form-data comme ce qu’un formulaire HTML enverrait, mais j’utilise un service Web REST et je veux continuer à utiliser JSON si possible.

Je suis d’accord avec Greg qu’une approche en deux phases est une solution raisonnable, mais je le ferais autrement. Je ferais:

 POST http://server/data/media body: { "Name": "Test", "Latitude": 12.59817, "Longitude": 52.12873 } 

Pour créer l’entrée de métadonnées et renvoyer une réponse comme:

 201 Created Location: http://server/data/media/21323 { "Name": "Test", "Latitude": 12.59817, "Longitude": 52.12873, "ContentUrl": "http://server/data/media/21323/content" } 

Le client peut alors utiliser cette ContentUrl et effectuer un PUT avec les données du fichier.

La bonne chose à propos de cette approche est que lorsque votre serveur démarre avec des volumes de données énormes, l’URL que vous renvoyez peut simplement pointer vers un autre serveur avec plus d’espace / capacité. Ou vous pouvez implémenter une approche de type round robin si la bande passante est un problème.

Le simple fait de ne pas encapsuler le corps de la requête dans JSON ne signifie pas qu’il n’est pas REST de pouvoir utiliser multipart/form-data pour publier le JSON et le fichier (ou plusieurs fichiers) en une seule requête:

 curl -F "metadata= 

côté serveur (en utilisant Python comme lingua franca de programmation ici):

 class AddFileResource(Resource): def render_POST(self, request): metadata = json.loads(request.args['metadata'][0]) file_body = request.args['file'][0] ... 

pour télécharger plusieurs fichiers, il est possible d'utiliser des "champs de formulaire" distincts pour chacun:

 curl -F "metadata= 

... auquel cas le code serveur aura request.args['file1'][0] et request.args['file2'][0]

ou réutiliser le même pour plusieurs:

 curl -F "metadata= 

... auquel cas request.args['files'] sera simplement une liste de longueur 2.

ou en fait passer plusieurs fichiers dans un seul champ en une seule fois:

 curl -F "metadata= 

... auquel cas request.args['files'] sera une chaîne contenant tous les fichiers, que vous devrez parsingr vous-même - vous ne savez pas comment le faire, mais je suis sûr que ce n'est pas difficile ou mieux utilisez simplement les approches précédentes.

La différence entre @ et < est que @ entraîne l'attachement du fichier en tant que téléchargement de fichier, tandis que < attache le contenu du fichier en tant que champ de texte.

PS Ce n'est pas parce que j'utilise curl comme un moyen de générer les requêtes POST que les mêmes requêtes HTTP ne peuvent pas être envoyées à partir d'un langage de programmation tel que Python ou d'un outil suffisamment performant.

Une façon d’aborder le problème consiste à faire du téléchargement un processus en deux phases. Tout d’abord, vous téléchargez le fichier lui-même en utilisant un POST, où le serveur renvoie un identifiant au client (un identifiant peut être le SHA1 du contenu du fichier). Ensuite, une seconde demande associe les métadonnées aux données du fichier:

 { "Name": "Test", "Latitude": 12.59817, "Longitude": 52.12873, "ContentID": "7a788f56fa49ae0ba5ebde780efe4d6a89b5db47" } 

L’inclusion du fichier firebase database 64 encodé dans la requête JSON proprement dite augmentera la taille des données transférées de 33%. Cela peut ou peut ne pas être important en fonction de la taille globale du fichier.

Une autre approche pourrait consister à utiliser un POST des données de fichiers brutes, mais à inclure toutes les métadonnées dans l’en-tête de requête HTTP. Cependant, cela tombe un peu en dehors des opérations REST de base et peut être plus compliqué pour certaines bibliothèques de clients HTTP.

Je me rends compte que c’est une très vieille question, mais j’espère que cela aidera quelqu’un d’autre, car je suis tombé sur ce post à la recherche de la même chose. J’ai eu un problème similaire, juste que mes métadonnées étaient un Guid et un Int. La solution est la même si. Vous pouvez simplement créer les métadonnées nécessaires dans l’URL.

Méthode d’acceptation POST dans votre classe “Controller”:

 public Task PostFile(ssortingng name, float latitude, float longitude) { //See http://stackoverflow.com/a/10327789/431906 for how to accept a file return null; } 

Ensuite, dans ce que vous enregistrez les routes, WebApiConfig.Register (HttpConfiguration config) pour moi dans ce cas.

 config.Routes.MapHttpRoute( name: "FooController", routeTemplate: "api/{controller}/{name}/{latitude}/{longitude}", defaults: new { } ); 

Si votre fichier et ses métadonnées créant une ressource, il est parfaitement correct de les télécharger dans une seule requête. Exemple de demande serait:

 POST https://target.com/myresources/resourcename HTTP/1.1 Accept: application/json Content-Type: multipart/form-data; boundary=-----------------------------28947758029299 Host: target.com -------------------------------28947758029299 Content-Disposition: form-data; name="application/json" {"markers": [ { "point":new GLatLng(40.266044,-74.718479), "homeTeam":"Lawrence Library", "awayTeam":"LUGip", "markerImage":"images/red.png", "information": "Linux users group meets second Wednesday of each month.", "fixture":"Wednesday 7pm", "capacity":"", "previousScore":"" }, { "point":new GLatLng(40.211600,-74.695702), "homeTeam":"Hamilton Library", "awayTeam":"LUGip HW SIG", "markerImage":"images/white.png", "information": "Linux users can meet the first Tuesday of the month to work out harward and configuration issues.", "fixture":"Tuesday 7pm", "capacity":"", "tv":"" }, { "point":new GLatLng(40.294535,-74.682012), "homeTeam":"Applebees", "awayTeam":"After LUPip Mtg Spot", "markerImage":"images/newcastle.png", "information": "Some of us go there after the main LUGip meeting, drink brews, and talk.", "fixture":"Wednesday whenever", "capacity":"2 to 4 pints", "tv":"" }, ] } -------------------------------28947758029299 Content-Disposition: form-data; name="name"; filename="myfilename.pdf" Content-Type: application/octet-stream %PDF-1.4 % 2 0 obj <>stream x+r 26S00SI2P0Qn F !i\ )%!Y0i@.k [ endstream endobj 4 0 obj <>>/Contents 2 0 R/Parent 3 0 R>> endobj 1 0 obj <> endobj 3 0 obj <> endobj 5 0 obj <> endobj 6 0 obj <> endobj xref 0 7 0000000000 65535 f 0000000250 00000 n 0000000015 00000 n 0000000338 00000 n 0000000138 00000 n 0000000389 00000 n 0000000434 00000 n trailer <]>> %iText-5.5.11 startxref 597 %%EOF -------------------------------28947758029299--