Cela va probablement être une question stupide mais je passe une de ces nuits. Dans une application, je développe une API RESTful et nous souhaitons que le client envoie des données en tant que JSON. Une partie de cette application nécessite que le client télécharge un fichier (généralement une image) ainsi que des informations sur l’image.
J’ai du mal à comprendre comment cela se produit en une seule demande. Est-il possible de base64 les données du fichier dans une chaîne JSON? Dois-je effectuer 2 publications sur le serveur? Devrais-je pas utiliser JSON pour cela?
En passant, nous utilisons Grails sur le backend et ces services sont accessibles aux clients mobiles natifs (iPhone, Android, etc.), si quelque chose fait la différence.
J’ai posé une question similaire ici:
Comment télécharger un fichier avec des métadonnées à l’aide d’un service Web REST?
Vous avez essentiellement trois choix:
multipart/form-data
et renvoyez un ID au client. Le client envoie ensuite les métadonnées avec l’ID et le serveur associe à nouveau le fichier et les métadonnées. Vous pouvez envoyer le fichier et les données dans une requête en utilisant le type de contenu multipart / form-data :
Dans de nombreuses applications, un formulaire peut être présenté à un utilisateur. L’utilisateur remplira le formulaire, y compris les informations saisies, générées par l’utilisateur ou incluses dans les fichiers sélectionnés par l’utilisateur. Lorsque le formulaire est rempli, les données du formulaire sont envoyées de l’utilisateur à l’application récepsortingce.
La définition de MultiPart / Form-Data est dérivée d’une de ces applications …
De http://www.faqs.org/rfcs/rfc2388.html :
“multipart / form-data” contient une série de parties. Chaque partie doit contenir un en-tête de disposition de contenu [RFC 2183] où le type de disposition est “form-data” et où la disposition contient un paramètre (supplémentaire) de “name”, la valeur de ce paramètre étant l’original. nom de champ dans le formulaire. Par exemple, une pièce peut contenir un en-tête:
Content-Disposition: données de formulaire; name = “user”
avec la valeur correspondant à l’entrée du champ “user”.
Vous pouvez inclure des informations de fichier ou des informations de champ dans chaque section entre les limites. J’ai implémenté avec succès un service RESTful qui nécessitait que l’utilisateur soumette des données et un formulaire, et que les données multipart / form fonctionnent parfaitement. Le service a été construit en utilisant Java / Spring, et le client utilisait C #, donc je n’ai malheureusement pas d’exemples Grails à vous donner concernant la configuration du service. Vous n’avez pas besoin d’utiliser JSON dans ce cas car chaque section “form-data” vous fournit un endroit pour spécifier le nom du paramètre et sa valeur.
La bonne chose à propos de l’utilisation de multipart / form-data est que vous utilisez des en-têtes définis par HTTP, vous vous en tenez donc à la philosophie REST consistant à utiliser les outils HTTP existants pour créer votre service.
Je sais que ce fil est assez ancien, cependant, il me manque une option. Si vous souhaitez envoyer des métadonnées (quel que soit le format) avec les données à télécharger, vous pouvez créer une seule requête multipart/related
.
Le type de support Multipart / Related est destiné aux objects composés composés de plusieurs parties de corps liées entre elles.
Vous pouvez vérifier la spécification RFC 2387 pour plus de détails.
Fondamentalement, chaque partie d’une telle requête peut avoir un contenu de type différent et toutes les parties sont liées d’une manière ou d’une autre (par exemple, une image et les métadonnées). Les pièces sont identifiées par une chaîne de délimitation et la dernière chaîne de démarcation est suivie de deux traits d’union.
Exemple:
POST /upload HTTP/1.1 Host: www.hostname.com Content-Type: multipart/related; boundary=xyz Content-Length: [actual-content-length] --xyz Content-Type: application/json; charset=UTF-8 { "name": "Sample image", "desc": "...", ... } --xyz Content-Type: image/jpeg [image data] [image data] [image data] ... --foo_bar_baz--
Je sais que cette question est ancienne, mais dans les derniers jours, j’avais cherché dans tout le Web pour résoudre cette même question. J’ai des graals Webservices REST et iPhone Client qui envoient des images, titre et description.
Je ne sais pas si mon approche est la meilleure, mais c’est tellement simple et facile.
Je prends une photo en utilisant le UIImagePickerController et envoie au serveur le NSData en utilisant les balises d’en-tête de request pour envoyer les données de l’image.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithSsortingng:@"myServerAddress"]]; [request setHTTPMethod:@"POST"]; [request setHTTPBody:UIImageJPEGRepresentation(picture, 0.5)]; [request setValue:@"image/jpeg" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"myPhotoTitle" forHTTPHeaderField:@"Photo-Title"]; [request setValue:@"myPhotoDescription" forHTTPHeaderField:@"Photo-Description"]; NSURLResponse *response; NSError *error; [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
Côté serveur, je reçois la photo en utilisant le code:
InputStream is = request.inputStream def receivedPhotoFile = (IOUtils.toByteArray(is)) def photo = new Photo() photo.photoFile = receivedPhotoFile //photoFile is a transient atsortingbute photo.title = request.getHeader("Photo-Title") photo.description = request.getHeader("Photo-Description") photo.imageURL = "temp" if (photo.save()) { File saveLocation = grailsAtsortingbutes.getApplicationContext().getResource(File.separator + "images").getFile() saveLocation.mkdirs() File tempFile = File.createTempFile("photo", ".jpg", saveLocation) photo.imageURL = saveLocation.getName() + "/" + tempFile.getName() tempFile.append(photo.photoFile); } else { println("Error") }
Je ne sais pas si j’ai des problèmes à l’avenir, mais maintenant, ça marche bien dans l’environnement de production.
Voici mon approche API (j’utilise exemple) – comme vous pouvez le voir, vous n’utilisez aucun File_id (identificateur de fichier téléchargé dans le serveur) dans l’API:
1.Créer un object ‘photo’ sur le serveur:
POST: /projects/{project_id}/photos params in: {name:some_schema.jpg, comment:blah} return: photo_id
Fichier 2.Upload (notez que “fichier” est au singulier car il n’y en a qu’un par photo):
POST: /projects/{project_id}/photos/{photo_id}/file params in: file to upload return: -
Et puis par exemple:
3.Lire la liste de photos
GET: /projects/{project_id}/photos params in: - return: array of objects: [ photo, photo, photo, ... ]
4. Lisez quelques photos
GET: /projects/{project_id}/photos/{photo_id} params in: - return: photo = { id: 666, name:'some_schema.jpg', comment:'blah'}
5.Lire le fichier photo
GET: /projects/{project_id}/photos/{photo_id}/file params in: - return: file content
Donc, la conclusion est que, tout d’abord, vous créez un object (photo) par POST, puis vous envoyez une requête de sécurité avec un fichier (à nouveau POST).
Objets FormData: Télécharger des fichiers à l’aide d’Ajax
XMLHttpRequest Level 2 ajoute la prise en charge de la nouvelle interface FormData. Les objects FormData permettent de construire facilement un ensemble de paires clé / valeur représentant les champs de formulaire et leurs valeurs, qui peuvent ensuite être facilement envoyés à l’aide de la méthode send () XMLHttpRequest.
function AjaxFileUpload() { var file = document.getElementById("files"); //var file = fileInput; var fd = new FormData(); fd.append("imageFileData", file); var xhr = new XMLHttpRequest(); xhr.open("POST", '/ws/fileUpload.do'); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { alert('success'); } else if (uploadResult == 'success') alert('error'); }; xhr.send(fd); }
Comme le seul exemple manquant est l’ exemple ANDROID , je vais l’append. Cette technique utilise un AsyncTask personnalisé qui doit être déclaré dans votre classe Activity.
private class UploadFile extends AsyncTask { @Override protected void onPreExecute() { // set a status bar or show a dialog to the user here super.onPreExecute(); } @Override protected void onProgressUpdate(Integer... progress) { // progress[0] is the current status (eg 10%) // here you can update the user interface with the current status } @Override protected Ssortingng doInBackground(Void... params) { return uploadFile(); } private Ssortingng uploadFile() { Ssortingng responseSsortingng = null; HttpClient httpClient = new DefaultHttpClient(); HttpPost httpPost = new HttpPost("http://example.com/upload-file"); try { AndroidMultiPartEntity ampEntity = new AndroidMultiPartEntity( new ProgressListener() { @Override public void transferred(long num) { // this sortinggger the progressUpdate event publishProgress((int) ((num / (float) totalSize) * 100)); } }); File myFile = new File("/my/image/path/example.jpg"); ampEntity.addPart("fileFieldName", new FileBody(myFile)); totalSize = ampEntity.getContentLength(); httpPost.setEntity(ampEntity); // Making server call HttpResponse httpResponse = httpClient.execute(httpPost); HttpEntity httpEntity = httpResponse.getEntity(); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode == 200) { responseSsortingng = EntityUtils.toSsortingng(httpEntity); } else { responseSsortingng = "Error, http status: " + statusCode; } } catch (Exception e) { responseSsortingng = e.getMessage(); } return responseSsortingng; } @Override protected void onPostExecute(Ssortingng result) { // if you want update the user interface with upload result super.onPostExecute(result); } }
Ainsi, lorsque vous souhaitez télécharger votre fichier, appelez simplement:
new UploadFile().execute();
@RequestMapping(value = "/uploadImageJson", method = RequestMethod.POST) public @ResponseBody Object jsongStrImage(@RequestParam(value="image") MultipartFile image, @RequestParam Ssortingng jsonStr) { -- use com.fasterxml.jackson.databind.ObjectMapper convert Json Ssortingng to Object }
Je voulais envoyer des chaînes au serveur principal. Je n’ai pas utilisé json et multipart, j’ai utilisé des parameters de requête.
@RequestMapping(value = "/upload", method = RequestMethod.POST) public void uploadFile(HttpServletRequest request, HttpServletResponse response, @RequestParam("uuid") Ssortingng uuid, @RequestParam("type") DocType type, @RequestParam("file") MultipartFile uploadfile)
Url ressemblerait
http://localhost:8080/file/upload?uuid=46f073d0&type=PASSPORT
Je passe deux parameters (uuid et type) avec téléchargement de fichier. J’espère que cela aidera ceux qui n’ont pas les données complexes de json à envoyer.
Veuillez vous assurer que vous avez l’importation suivante. Bien sûr, d’autres importations standard
import org.springframework.core.io.FileSystemResource void uploadzipFiles(Ssortingng token) { RestBuilder rest = new RestBuilder(connectTimeout:10000, readTimeout:20000) def zipFile = new File("testdata.zip") def Id = "001G00000" MultiValueMap form = new LinkedMultiValueMap() form.add("id", id) form.add('file',new FileSystemResource(zipFile)) def urld ='''http://URL'''; def resp = rest.post(urld) { header('X-Auth-Token', clientSecret) contentType "multipart/form-data" body(form) } println "resp::"+resp println "resp::"+resp.text println "resp::"+resp.headers println "resp::"+resp.body println "resp::"+resp.status }
Si vous développez un serveur de repos, vous pouvez le faire
{"file_url":"http://cockwombles.com/blah.jpg"}