tutorial - Publication d'un fichier et de données associées dans un WebService RESTful, de préférence en tant que JSON




web service rest tutorial (7)

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:

  1. Base64 encoder le fichier, au prix d'augmenter la taille des données d'environ 33%.
  2. Envoyez d'abord le fichier dans un POST multipart/form-data et renvoyez un ID au client. Le client envoie ensuite les métadonnées avec l'ID et le serveur réassocie le fichier et les métadonnées.
  3. Envoyez d'abord les métadonnées et renvoyez un identifiant au client. Le client envoie ensuite le fichier avec l'ID et le serveur réassocie le fichier et les métadonnées.

Cela va probablement être une question stupide, mais je vais avoir une de ces nuits. Dans une application, je développe une API RESTful et nous voulons que le client envoie des données au format 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 à trouver comment cela se passe dans une seule requête. Est-il possible de Base64 les données de fichier dans une chaîne JSON? Est-ce que je vais devoir effectuer 2 messages sur le serveur? Ne devrais-je pas utiliser JSON pour cela?

En guise de remarque, nous utilisons Grails sur le backend et ces services sont accessibles par les clients mobiles natifs (iPhone, Android, etc), si l'un de ces éléments fait une différence.


Je sais que ce fil est assez vieux, cependant, il me manque une option. Si vous avez des métadonnées (quel que soit le format) que vous souhaitez envoyer avec les données à télécharger, vous pouvez effectuer une seule requête en plusieurs parties.

Le type Multipart / Related media est destiné aux objets composés composés de plusieurs parties de corps interdépendantes.

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 avec un type différent et toutes les parties sont en quelque sorte liées (par exemple une image et des métadonnées). Les parties sont identifiées par une chaîne de délimitation et la chaîne de délimitation finale 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--

Objets FormData: Télécharger des fichiers à l'aide d'Ajax

XMLHttpRequest Level 2 ajoute le support pour la nouvelle interface FormData. Les objets FormData permettent de créer 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 XMLHttpRequest send ().

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);
}

https://developer.mozilla.org/en-US/docs/Web/API/FormData


Puisque le seul exemple manquant est l' exemple ANDROID , je vais l'ajouter. Cette technique utilise une asyncTask personnalisée qui doit être déclarée dans votre classe Activity.

private class UploadFile extends AsyncTask<Void, Integer, String> {
    @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 (e.g. 10%)
        // here you can update the user interface with the current status
    }

    @Override
    protected String doInBackground(Void... params) {
        return uploadFile();
    }

    private String uploadFile() {

        String responseString = 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 trigger 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) {
                responseString = EntityUtils.toString(httpEntity);
            } else {
                responseString = "Error, http status: "
                        + statusCode;
            }

        } catch (Exception e) {
            responseString = e.getMessage();
        }
        return responseString;
    }

    @Override
    protected void onPostExecute(String result) {
        // if you want update the user interface with upload result
        super.onPostExecute(result);
    }

}

Donc, quand vous voulez télécharger votre fichier, appelez simplement:

new UploadFile().execute();

Veuillez vous assurer que vous avez l'importation suivante. Ofcourse autres importations standard

import org.springframework.core.io.FileSystemResource


    void uploadzipFiles(String token) {

        RestBuilder rest = new RestBuilder(connectTimeout:10000, readTimeout:20000)

        def zipFile = new File("testdata.zip")
        def Id = "001G00000"
        MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>()
        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
    }

Voici mon API d'approche (j'utilise l'exemple) - comme vous pouvez le voir, je n'utilise aucun fichier file_id (identyicator de fichier uploadé dans le serveur) dans l'API:

1.Créer un objet 'photo' sur le serveur:

POST: /projects/{project_id}/photos   
params in: {name:some_schema.jpg, comment:blah}
return: photo_id

2.Télécharger le fichier (notez que 'fichier' est au singulier car il est seulement 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 des photos

GET: /projects/{project_id}/photos
params in: -
return: array of objects: [ photo, photo, photo, ... ]

4. Lisez quelques détails de la photo

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, d'abord vous créez un objet (photo) par POST, et ensuite vous envoyez une requête secod avec le fichier (encore POST).


@RequestMapping(value = "/uploadImageJson", method = RequestMethod.POST)
    public @ResponseBody Object jsongStrImage(@RequestParam(value="image") MultipartFile image, @RequestParam String jsonStr) {
-- use  com.fasterxml.jackson.databind.ObjectMapper convert Json String to Object
}






file-upload