281 votes

Comment télécharger un fichier avec des métadonnées en utilisant un service web REST ?

J'ai un service web REST qui expose actuellement cette URL :

http://server/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 Media.

J'ai maintenant besoin de la possibilité de télécharger un fichier en même temps que les métadonnées du média. Quelle est la meilleure façon de procéder ? Je pourrais introduire une nouvelle propriété appelée file et coder le fichier en base64, mais je me demandais s'il y avait un meilleur moyen.

Il y a aussi l'utilisation multipart/form-data comme ce qu'un formulaire HTML enverrait, mais j'utilise un service web REST et je veux m'en tenir à l'utilisation de JSON si possible.

45 votes

Il n'est pas vraiment nécessaire de s'en tenir à l'utilisation de JSON pour disposer d'un service web RESTful. REST est en fait tout ce qui suit les grands principes des méthodes HTTP et quelques autres règles (sans doute non standardisées).

7voto

Mike Ezzati Points 1136

Si votre fichier et ses métadonnées créent une seule ressource, il est parfaitement possible de les télécharger tous les deux en une seule demande. Un 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
<</Length 57/Filter/FlateDecode>>stream
x+r
26S00SI2P0Qn
F
!i\
)%!Y0i@.k
[
endstream
endobj
4 0 obj
<</Type/Page/MediaBox[0 0 595 842]/Resources<</Font<</F1 1 0 R>>>>/Contents 2 0 R/Parent 3 0 R>>
endobj
1 0 obj
<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding/WinAnsiEncoding>>
endobj
3 0 obj
<</Type/Pages/Count 1/Kids[4 0 R]>>
endobj
5 0 obj
<</Type/Catalog/Pages 3 0 R>>
endobj
6 0 obj
<</Producer(iTextSharp 5.5.11 2000-2017 iText Group NV \(AGPL-version\))/CreationDate(D:20170630120636+02'00')/ModDate(D:20170630120636+02'00')>>
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
<</Size 7/Root 5 0 R/Info 6 0 R/ID [<c7c34272c2e618698de73f4e1a65a1b5><c7c34272c2e618698de73f4e1a65a1b5>]>>
%iText-5.5.11
startxref
597
%%EOF

-------------------------------28947758029299--

0voto

Will59 Points 85

Pour compléter la réponse de Ccleve, si vous utilisez superagent / express / multer, construisez votre requête multipart du côté frontal en faisant quelque chose comme ceci :

superagent
    .post(url)
    .accept('application/json')
    .field('myVeryRelevantJsonData', JSON.stringify({ peep: 'Peep Peep!!!' }))
    .attach('myFile', file);

cf https://visionmedia.github.io/superagent/#multipart-requests .

Du côté express, ce qui a été adopté comme field se retrouvera dans req.body après avoir fait :

app.use(express.json({ limit: '3MB' }));

Votre itinéraire devrait comprendre quelque chose comme ceci :

const multerMemStorage = multer.memoryStorage();
const multerUploadToMem = multer({
  storage: multerMemStorage,
  // Also specify fileFilter, limits...
});

router.post('/myUploads',
  multerUploadToMem.single('myFile'),
  async (req, res, next) => {
    // Find back myVeryRelevantJsonData :
    logger.verbose(`Uploaded req.body=${JSON.stringify(req.body)}`);

    // If your file is text:
    const newFileText = req.file.buffer.toString();
    logger.verbose(`Uploaded text=${newFileText}`);
    return next();
  },
  ...

Une chose à garder à l'esprit cependant est cette note de la doc Multer, concernant le stockage sur disque :

Notez que req.body n'a peut-être pas encore été entièrement rempli. Cela dépend de l'ordre dans lequel le client transmet les champs et les fichiers au serveur.

Je suppose que cela signifie qu'il ne serait pas fiable de, disons, calculer le répertoire/nom de fichier cible sur la base des métadonnées json transmises dans le fichier.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X