{"id":73,"date":"2021-06-23T18:31:16","date_gmt":"2021-06-23T17:31:16","guid":{"rendered":"http:\/\/guillemrueda.com\/blog\/?p=73"},"modified":"2021-06-23T18:31:16","modified_gmt":"2021-06-23T17:31:16","slug":"entitats-amb-fitxers-tot-junt-o-per-parts","status":"publish","type":"post","link":"https:\/\/guillemrueda.com\/blog\/2021\/06\/23\/entitats-amb-fitxers-tot-junt-o-per-parts\/","title":{"rendered":"Entitats amb fitxers: tot junt o per parts?"},"content":{"rendered":"\n<p>Hi ha dues propostes per a pujar (actualitzar-les tamb\u00e9) unes entitats que porten vinculades a la imatge:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>[POST] \/api\/v1\/imatge amb un cos html serialitzat tipus { desciption: &#8220;&#8221;,  tags: [&#8220;&#8221;, &#8220;&#8221;,&#8221;&#8221;] } <\/li><li>[POST] \/api\/v1\/image\/{id}\/upload amb un multipart\/form-www image=@adre\u00e7aImatge<\/li><\/ul>\n\n\n\n<p>Amb:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>[POST] \/api\/v1\/imatge amb un multipart\/form-data description=&#8221;&#8221; ; tags=[&#8220;&#8221;,&#8221;&#8221;,&#8221;&#8221;]; image=@adre\u00e7aImatge<\/li><\/ul>\n\n\n\n<p>I aqu\u00ed \u00e9s on premia qui ha de fer \u00fas de la API, quina utilitzaci\u00f3 se li dona. El fet de treballar amb entitats sempre \u00e9s com all\u00f2 de que per a un martell tot s\u00f3n claus. Haur\u00edem de separar:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Amb quina recurr\u00e8ncia es pujaran imatges o ser\u00e0 un fet \u00fanic?<\/li><li>Les aplicacions clients volen crear l&#8217;entitat amb la imatge?<\/li><li>Les aplicacions clients com processen els multipart\/form-data?<\/li><\/ul>\n\n\n\n<p>De les tres preguntes, les dues primeres s\u00f3n de disseny i la tercera d&#8217;arquitectura. Per all\u00f2 del que els <em>backends <\/em>poden viure a esquenes de la realitat si ofereixen unes portes definides. S\u00f3n importants perqu\u00e8 poden encorsetar bastant el processament de les dades (no \u00e9s el mateix un <em>form <\/em>qu\u00e8 un <em>body <\/em>serialitzat).<\/p>\n\n\n\n<p>Amb NodeJS tenim <a href=\"https:\/\/www.npmjs.com\/package\/multer\">multer<\/a> qu\u00e8 a mode intermediari (middleware) ens pot emmagatzemar la imatge en un directori, i posar-nos les dades del <em>form-data<\/em> al <em>body <\/em>com a propietats. En el seg\u00fcent exemple es fa servir la segona opci\u00f3:<\/p>\n\n\n\n<p>routes.js<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:13px\"><code>  app.post(\"\/v1\/imatge\", imatgeController.uploadImg,  imatgeController.save);<\/code><\/pre>\n\n\n\n<p>imatgeController.js<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:13px\"><code>const multer = require('multer');\r\n\rconst fs = require('fs');\r\n\r\nconst imageStorage = multer.diskStorage({\r\n  destination: function (req, file, cb) {\r\n\r\n    if (!fs.existsSync('.\/imageUploads')) {\r\n      fs.mkdirSync('.\/imageUploads');\r\n    }\r\n\r\n    cb(null, '.\/imageUploads');\r\n  },\r\n  filename: function (req, file, cb) {\r\n    cb(null, file.originalname);\r\n  }\r\n});\r\n\r\nexports.uploadImg = multer({ storage: imageStorage }).single('image');\nexports.save = async (req, res) => {\n\nvar entity = { description: req.body.description, image: req.file.filename, tags: req.body.tags };\n...\n}\n\n<\/code><\/pre>\n\n\n\n<p>El fet de que podem sobrecarregar amb diverses funcions les rutes tamb\u00e9 ens facilitar\u00e0 altres tipus de processaments (autenticaci\u00f3, per exemple), per\u00f2 aix\u00f2 ja \u00e9s cosa m\u00e9s de NodeJS i ExpressJS.<\/p>\n\n\n\n<p>La soluci\u00f3 ens sobrecarrega bastant el qu\u00e8 \u00e9s la entitat: la carrega de imatge per un costat, qu\u00e8 pot tenir m\u00e9s l\u00f2gica (compressi\u00f3, tractament, despla\u00e7ament a un lloc remot) i pot fer m\u00e9s pesat del que \u00e9s en si el pujar unes dades per a una entitat.<\/p>\n\n\n\n<p>Per a fer un punt com\u00fa entre client i servidor, el codi swagger.json descriur\u00e0 els camps com a formData parant especial atenci\u00f3 en el type:<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:13px\"><code>\"\/v1\/imatges\": {\r\n      \"post\": {\r\n        \"tags\": &#91;\r\n          \"imatges\"\r\n        ],\r\n        \"summary\": \"Afegir una imatge\",\r\n        \"description\": \"Ruta per afegir una nova imatge\",\r\n        \"consumes\": &#91;\r\n          \"image\"\r\n        ],\r\n        \"parameters\": &#91;\r\n          {\r\n            \"name\": \"imatge\",\r\n            \"in\": \"formData\",\r\n            \"description\": \"afegir nova imatge\",\r\n            \"required\": \"true\",\r\n            \"type\": \"file\"\r\n          },\r\n          {\r\n            \"name\": \"descripcio\",\r\n            \"in\": \"formData\",\r\n            \"description\": \"descripci\u00f3 de la imatge\",\r\n            \"required\": \"true\",\r\n            \"type\": \"string\"\r\n          }\r\r\n        ], \n...<\/code><\/pre>\n\n\n\n<p>Per al client, fem l&#8217;exemple de un swagger tipificat i l&#8217;utilitzem en un client, en aquest cas, NSwag amb dest\u00ed C#. El resultat \u00e9s una funci\u00f3 utilitzable per aquest client:<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:13px\"><code>        \/\/\/ &lt;summary>Create a banner&lt;\/summary>\n        \/\/\/ &lt;param name=\"image\">Adding new publication.&lt;\/param>\n        \/\/\/ &lt;param name=\"name\">Banner name&lt;\/param>\n        \/\/\/ &lt;param name=\"isDisabled\">Is disabled?&lt;\/param>\n        \/\/\/ &lt;param name=\"analyticCode\">Analytic Code&lt;\/param>\n        \/\/\/ &lt;exception cref=\"SwaggerException\">A server side error occurred.&lt;\/exception>\n        public System.Threading.Tasks.Task Banners4Async(FileParameter image, string name, bool isDisabled, string analyticCode)\n        {\n            return Banners4Async(image, name, isDisabled, analyticCode, System.Threading.CancellationToken.None);\n        }<\/code><\/pre>\n\n\n\n<p>Quina seria la difer\u00e8ncia de fer-lo per separat? En un altre post&#8230;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hi ha dues propostes per a pujar (actualitzar-les tamb\u00e9) unes entitats que porten vinculades a la imatge: [POST] \/api\/v1\/imatge amb un cos html serialitzat tipus { desciption: &#8220;&#8221;, tags: [&#8220;&#8221;, &#8220;&#8221;,&#8221;&#8221;] } [POST] \/api\/v1\/image\/{id}\/upload amb un multipart\/form-www image=@adre\u00e7aImatge Amb: [POST] \/api\/v1\/imatge amb un multipart\/form-data description=&#8221;&#8221; ; tags=[&#8220;&#8221;,&#8221;&#8221;,&#8221;&#8221;]; image=@adre\u00e7aImatge I aqu\u00ed \u00e9s on premia qui ha &hellip; <a href=\"https:\/\/guillemrueda.com\/blog\/2021\/06\/23\/entitats-amb-fitxers-tot-junt-o-per-parts\/\" class=\"more-link\">Continua la lectura de <span class=\"screen-reader-text\">Entitats amb fitxers: tot junt o per parts?<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7,9,10,6],"tags":[],"class_list":["post-73","post","type-post","status-publish","format-standard","hentry","category-net-programacio","category-api","category-nodejs","category-programacio"],"_links":{"self":[{"href":"https:\/\/guillemrueda.com\/blog\/wp-json\/wp\/v2\/posts\/73","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/guillemrueda.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/guillemrueda.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/guillemrueda.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/guillemrueda.com\/blog\/wp-json\/wp\/v2\/comments?post=73"}],"version-history":[{"count":1,"href":"https:\/\/guillemrueda.com\/blog\/wp-json\/wp\/v2\/posts\/73\/revisions"}],"predecessor-version":[{"id":74,"href":"https:\/\/guillemrueda.com\/blog\/wp-json\/wp\/v2\/posts\/73\/revisions\/74"}],"wp:attachment":[{"href":"https:\/\/guillemrueda.com\/blog\/wp-json\/wp\/v2\/media?parent=73"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/guillemrueda.com\/blog\/wp-json\/wp\/v2\/categories?post=73"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/guillemrueda.com\/blog\/wp-json\/wp\/v2\/tags?post=73"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}