PC SOFT

FOROS PROFESIONALES
WINDEVWEBDEV y WINDEV Mobile

Inicio → WINDEV 2024 → Téléchargement de documents reçu d'une application mobile android
Téléchargement de documents reçu d'une application mobile android
Iniciado por Jennifer, 09,abr. 2024 12:04 - 21 respuestas
Miembro registrado
16 mensajes
Publicado el 09,abril 2024 - 12:04
Bonjour,

Je travaille en partenariat avec une société qui développe des applications mobiles. Une des fonctionnalités développées dans l'application mobile consiste à joindre des photos lors d'un échec de livraison. A ce moment-là, l'application mobile fait appel à un webservice (POST) que j'ai développé pour envoyer les documents (multi-part). Plusieurs documents peuvent être envoyés dans un unique appel service.

Je reste coincée depuis plusieurs jours sur le moyen de récupérer (sauvegarder) les documents qui me sont transmis.

Je ne sais pas dans quelle propriété est stocké le contenu du document. J'ai bien accès aux propriétés Content-Disposition et Content-Type mais cela ne m'aide pas vraiment.

Avez-vous des suggestions à faire. Je ne trouve pas dans l'aide.

Merci à tous,
Miembro registrado
3.892 mensajes
Popularité : +227 (347 votes)
Publicado el 09,abril 2024 - 12:45
Bonjour,
C'est toi qui a conçu le WS donc tu connais la structure utilisée.
A toi d'utiliser la même structure lors de la désérialisation.

--
Il y a peut être plus simple, mais, ça tourne
Miembro registrado
450 mensajes
Popularité : +31 (43 votes)
Publicado el 09,abril 2024 - 13:32
Bonjour

voici comment je fais pour récupérer une image envoyé d'un mobile à un ws que j'ai dev :

reponse est un JSON = WebserviceParamètre(paramBuffer)

chemin est une chaîne = reponse.chemin_fichier + [fSep()] + reponse.nom_fichier
buffer_image est un Buffer = Decode(reponse.image, encodeBASE64)
fSauveBuffer(chemin,buffer_image)


cdlt

DG
Miembro registrado
16 mensajes
Publicado el 09,abril 2024 - 14:19
Bonjour Voroltinquo,

Merci pour ton aide. J'ai l'impression de ne pas tout comprendre encore mais j'avance en m'aidant de Postman.
Quand je comprendrai le fonctionnement via Postman, je pourrai me pencher sur la liaison appli mobile webservice.
J'ai donc un appel de mon service dans Postman. Je met plusieurs documents (file) dans le form-data body.
Je reçois les infos sous cette forme :

Screenshot_20240409-132909.png;C:\WEBDEV 28\TmpUpload\WEBDEV_0_15124_-2011105307_31099511.upl

J'ai mis 2 photos et je ne vois que le nom d'une seule. Et quand je vais dans le dossier où est sensé se trouver le .upl je ne vois rien.
Que dois-je faire de ça ?

Merci de ton aide,
Miembro registrado
16 mensajes
Publicado el 09,abril 2024 - 14:39
Bonjour Dimitri G,

Si je met la ligne de code
reponse est un JSON = WebserviceParamètre(paramBuffer)

J'ai une erreur dans Posman "Le contenu de la requête est vide".

J'avoue ne pas y comprendre grand chose.
Mensaje modificado, 09,abril 2024 - 14:39
Miembro registrado
16 mensajes
Publicado el 09,abril 2024 - 15:17
J'ai combiné vos deux suggestions et j'arrive bien à enregistrer l'image envoyée mais quand j'essaye de l'ouvrir j'ai un message "Paint can not read this file". Il faut aussi que je puisse enregistrer plusieurs images en un seul appel du WS.
Miembro registrado
3.892 mensajes
Popularité : +227 (347 votes)
Publicado el 09,abril 2024 - 15:25
Je suppose que ta structure contient un tableau de buffers, destiné à recevoir les images

--
Il y a peut être plus simple, mais, ça tourne
Miembro registrado
16 mensajes
Publicado el 09,abril 2024 - 15:42
Oui je viens d'adapter car ce n'était pas le cas.
Mais le service reçoit les infos sous cette forme :
sFiles est une chaîne = WebserviceParamètre("file")
sFiles => Screenshot_20240409-132909.png;C:\WEBDEV 28\TmpUpload\WEBDEV_0_15124_-2011105307_31099511.upl

que je reçoive une ou plusieurs photos je n'ai toujours que ça comme info.
Comment savoir le nombre de photos ? Et quel nom leur donner ?
Miembro registrado
450 mensajes
Popularité : +31 (43 votes)
Publicado el 09,abril 2024 - 15:47
pour exemple et même si cela ne répond pas complètement à votre problème, voici le code coté mobile qui permet d'envoyer une image au ws,

rq est un restRequête
rp est une restRéponse


v est un JSON
v.nom_fichier = "testimage.jpg"
v.chemin_fichier = "\\SRV\Media\Photo"

image2 est une Image = "d:\phototest.jpg"
bufBuffer est un Buffer = image2
v.image = Encode(bufBuffer,encodeBASE64)



rq.URL = "http://monwebservice/enregistrer_media"
rq.Méthode = httpPost
rq.ContentType = typeMimeJSON

rq.Contenu = v
rp = RESTEnvoie(rq)



FIN
Miembro registrado
16 mensajes
Publicado el 09,abril 2024 - 16:07
Ok, je comprend mieux en ayant les deux vues.
Mais effectivement ça ne m'aide pas beaucoup pour résoudre mon cas.
Miembro registrado
16 mensajes
Publicado el 09,abril 2024 - 16:29
POST /xxxxxxx
user-agent: Dart/3.3 (dart:io)
content-type: multipart/form-data; boundary=dart-http-boundary-SFLrV_WnbR._bfPmI3p_HD72AE-AOBr9jXC-xGK_9QDJ3lK23z-
accept-encoding: gzip
content-length: 823212
token: xxxxxxxxxxxxxxxxxxxxxxxxx
host: xxxxxxxxxxxxxxxxxxxxxxxxxxx

--dart-http-boundary-SFLrV_WnbR._bfPmI3p_HD72AE-AOBr9jXC-xGK_9QDJ3lK23z-
content-type: application/octet-stream
content-disposition: form-data; name="CAP9126106585719849515.jpg"; filename="CAP9126106585719849515.jpg"

ÿØÿá=¿Exif  MM *          Р     à         ž       ²              ¸       À(       2       È       ‡i       Üˆ%      È  ÎZebra Technologies  TC58     H      H   2024:04:02 19:45:03  "‚š      z‚      ‚ˆ"       ˆ'    ‚       0220      Š      ž      ²      º‘     ’ 
     Â’      Ê’ 
     Ò’ 
     Ú’      â’       ’       ’        ’
      ê’†    Ÿ  ò’      ’’‘      š’’      ¢      0100               Р      à       ©¢       £       ¤        ¤        ¤       ¤             (;šÊ    ¥   d2024:04:02 19:45:03 2024:04:02 19:45:03 +02:00  +02:00    ú  è      d   p   d             d  ‚  èsensor mode:4, UC:1, feature:0 0, scene:0, effect:0
HDR scene detected 0, HDR failed 0
num HDR frames 0 EV table [0, 0, 0]
MFNR Disable MFNR scene detected 1
  239561  239561  239561       R98      0100                    @        ð                    @      H(             P      9g       H      H   ÿØÿÛ „   !"$"$ÿ
--dart-http-boundary-SFLrV_WnbR._bfPmI3p_HD72AE-AOBr9jXC-xGK_9QDJ3lK23z-
content-type: application/octet-stream
content-disposition: form-data; name="CAP901797041637942418.jpg"; filename="CAP901797041637942418.jpg"

ÿØÿáAaExif  MM *          Р     à         ž       ²              ¸       À(       2       È       ‡i       Üˆ%      ì  òZebra Technologies  TC58     H      H   2024:04:02 19:45:09  "‚š      z‚      ‚ˆ"       ˆ'     õ       0220      Š      ž      ²      º‘     ’ 
     Â’      Ê’ 
     Ò’ 
     Ú’      â’       ’       ’        ’
      ê’†    Ã  ò’      ¶’‘      ¾’’      Æ      0100               Р      à       Í¢       £       ¤        ¤        ¤       ¤             (;šÊ    ¥   d2024:04:02 19:45:09 2024:04:02 19:45:09 +02:00  +02:00    ú  è      d   °   d             d  ‚  èsensor mode:4, UC:1, feature:0 0, scene:0, effect:0
HDR scene detected 0, HDR failed 0
num HDR frames 1139638272 EV table [1137344512, 1137934336, 1138524160]
MFNR Disable MFNR scene detected 1
  599463  599463  599463       R98      0100                    @        ð                    d      l(             t      <å       H      H   ÿØÿÛ „   !"$"$ÿÀ  ð@" ÿÄ¢          
   } !1AQa"q2‘¡#B±ÁRÑð$3br‚
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖ×ØÙÚáâãäåæçèéêñòóôõö÷øùú       
  w !1AQaq"2B‘¡±Á #3RðbrÑ
$4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³


Voici un exemple de ce que l'application m'envoie. Entre chaque "--dart-http-boundary" se trouve les infos liées à une image.
Je ne sais pas quelle méthode WinDev utiliser pour simplement récupérer content-type et content-disposition pour chaque image.
J'ai utilisé WebserviceParamètre mais en vain.
Je fais appel à la méthode WebserviceLitEnteteHttp pour accéder au content-type et content-disposition généraux. ça fonctionne.

Et c'est encore plus flou pour récupérer le contenu de l'image qui semble associé à aucune variable.

J'espère que cet exemple vous permettra de cerner un peu mieux mes difficultés.
Miembro registrado
3.892 mensajes
Popularité : +227 (347 votes)
Publicado el 09,abril 2024 - 16:47
Dans un premier temps, WebserviceParamètre renvoie un buffer et non une chaine.
C'est ce buffer que tu va désérialiser.
A partir de là tu aura accès au tableau contenant tes images, ou une structure contenant l'image et les infos.
Le nombre d'image est la taille du tableau (son nombre d'occurrences)
Grosso modo, ta structure/classe dois ressembler à çà
STImage est structure
m_saNomImage est chaîne
m_bufImage est Buffer
FIN
STEnvoi est une structure
//Membres divers
m_tabListeImage est tableau de STImage
FIN


A la volée, les code ressemblent à ça :
//Côté émission
stEnvoi est STEnvoi
bufEnvoi est Buffer
saURLRequete est chaîne

//Ajout des images dans stEnvoi.m_tabListeImage
Sérialise(stEnvoi,bufEnvoi,psdBinaire) //La transmission d'un buffer binaire est plus rapide par la radio

HTTPRequête(saURLRequete,"","",bufEnvoi)
//------------------------------------------------------------------------------------------------------
//Côté réception
bufMsgReçu est un Buffer
stMsg est STEnvoi

bufMsgReçu=WebserviceParamètre(paramBuffer)
Désérialise(stMsg,bufMsgReçu,psdBinaire)

//Traitement de stMsg.m_tabListeImages


--
Il y a peut être plus simple, mais, ça tourne
Mensaje modificado, 09,abril 2024 - 16:48
Publicado el 10,abril 2024 - 16:58
Bonjour,
Voici un exemple de code qui fonctionne parfaitement.
Il faut passer les images/photos/documents en Json afin de récupérer en même temps d'autres informations, par exemple le nom du fichier etc...

Code d'envoi depuis le mobile
// ENVOYER LA PIECE JOINTE
ParamId est Variant
ParamId.NumChantier = gnum_chantier
ParamId.NumCommande = gnum_commande
SI NomFichier_1 <> "" ALORS
// PARAMETRE ID
ParamId.NomFichier_1 = NomFichier_1
ParamId.MonFichier_1 = FichierEncoder_1
SINON
ParamId.NomFichier_1 = ""
ParamId.MonFichier_1 = ""
FIN
ElementEnJSON est une chaîne ANSI = VariantVersJSON(ParamId)
// CODE ENVOI EN WS
MaQuestion est un restRequête
MaRéponse est une restRéponse
MaQuestion.URL = "http://"+Mon_IP_serveur+"/nom du webservice"
MaQuestion.Contenu = ElementEnJSON
MaQuestion.Méthode = httpPost
MaQuestion.ContentType = "application/json"
MaRéponse = RESTEnvoie(MaQuestion)


Code du webservice de WD pour enregistrer le fichier
Procedure Nom du la Procedure()
// PROCEDURE DECODER EN BASE64
ElementEnJSON est une chaîne ANSI = WebserviceParamètre(paramBuffer)
ParamId est un Variant = JSONVersVariant(ElementEnJSON)
SI ParamId.NomFichier_1 <> "" ALORS
SI fRepExiste(societe.chemin_chantier + "\" + ParamId.NumChantier + "\Commande\" + ParamId.NumCommande) = Faux ALORS
fRepCrée(societe.chemin_chantier + "\" + ParamId.NumChantier + "\Commande\" + ParamId.NumCommande)
FIN
fSauveBuffer(societe.chemin_chantier + "\" + ParamId.NumChantier + "\Commande\" + ParamId.NumCommande + "\" + ParamId.NomFichier_1, Decode(ParamId.MonFichier_1, encodeBASE64))
SINON
FIN
RENVOYER Vrai


Et éventuellement si vous voulez le code pour sélectionner les fichiers depuis le mobile. Il se trouve dans le forum mobile
https://forum.pcsoft.fr/fr-FR/pcsoft.fr.windevmobile/50082-selectionner-image-fichier-pdf/read.awp

Bon développement.
Miembro registrado
16 mensajes
Publicado el 15,abril 2024 - 16:57
Bonjour,

Merci beaucoup pour vos suggestions de code. J'ai essayé plusieurs choses mais je n'ai pas encore la solution pour répondre à mon problème.

L'utilisation de paramBuffer en tant que paramètre de la méthode WebserviceParamètre ne fonctionne pas chez moi.

J'ai récupéré le code implémenté dans l'application mobile pour cet envoi de pièces jointes justement. C'est en Flutter + Dart :
var request = MultipartRequest('Post', url);
for (var a IN attachments) {
request.files.add(await MultipartFile.fromPath(
a.filename,
a.path,
filename: a.filename,
));
}


Ainsi, je comprends bien que toutes les pièces jointes sont ajoutées dans le tableau de files avec pour chacun d'eux un nom de fichier (filename) et le chemin où est stocké le fichier en question (path).

De mon côté, dans le WS Windev j'ai fait ceci
STAttachment est une structure
NomFicher est une chaîne
ContenuFichier est un Buffer
FIN
STUploadResponse est une structure
Files est un tableau de STAttachment
FIN
stUploadRep est un STUploadResponse
bufFiles est un Buffer = WebserviceParamètre("files")
Désérialise(stUploadRep,bufFiles,psdBinaire)
fLogger.InfoMsg(ChaîneConstruit("ATTACHMENTS ParamId : %1",stUploadRep.Files.Occurrence()))


Je ne suis pas certaine des noms à utiliser de mon côté. En tout cas pour le moment ça coince et le message InfoMsg ne s'écrit pas.

Merci de votre aide.
Miembro registrado
397 mensajes
Popularité : +13 (13 votes)
Publicado el 15,abril 2024 - 17:17
Bonjour,

A la vue de la requête client HTTP du post #11, on est sur de l'upload classique en multipart/form-data, c'est-à-dire comme avec un champ d'upload en WebDev. Dans ce cas, en règle générale, on a des aides (par exemple, en PHP il y a la variable globale $_FILES, en WebDev "classique" hors-webservice, on a les fonction UploadXXX()). Mais pas en webservice.

Voilà ce qu'il faut à peu près faire :
- depuis l'entête général "Content-Type", récupérer la "boundary" utilisée pour le reste de la requête : c'est cette boundary (en français "limite", "borne"), générée dynamiquement par le client, qui permet de séparer le contenu de la requête en différents morceaux
- récupérer le buffer représentant le contenu de la requête : ça peut être via WebserviceParamètre(paramBuffer), ou par un paramètre de type Buffer dans les arguments de la procédure (et la liaison dans la description du point d'entrée)
- découper le buffer en plusieurs morceaux utilisant la chaine "--" + boundary
- Pour chaque morceaux, vous allez donc avoir plusieurs éléments :
- un content-type qui donne le type de la data qui suit,
- un content-disposition qui en donne plusieurs informations, comme le nom du fichier
- puis, après un saut de ligne, vous aurez le buffer correspondant au fichier
- avec tous ces bouts vous devriez pouvoir alors sauvegarder votre fichier.
Miembro registrado
16 mensajes
Publicado el 16,abril 2024 - 08:24
Bonjour bchnaudet,

Merci beaucoup pour ton explications détaillées des étapes à faire. Je pense avoir compris le principe.
Voici ce que j'ai mis en place pour le moment :

sContentTypeGlobal est une chaîne = WebserviceLitEntêteHTTP("Content-Type")
fLogger.InfoMsg(ChaîneConstruit("ATTACHMENTS Content-Type : %1",sContentTypeGlobal))
sBoundary est une chaîne = ExtraitChaîne(sContentTypeGlobal,2,"boundary=",DepuisDébut)
fLogger.InfoMsg(ChaîneConstruit("ATTACHMENTS sBoundary : %1",sBoundary))

bufCont est un Buffer = WebserviceParamètre(paramBuffer)
fLogger.InfoMsg(ChaîneConstruit("ATTACHMENTS Taille(bufCont) : %1",Taille(bufCont)))
sImg1 est une chaîne = ExtraitChaîne(bufCont,2,sBoundary,DepuisDébut)
fLogger.InfoMsg(ChaîneConstruit("ATTACHMENTS sImg1 : %1",sImg1))
sContentTypeImg1 est une chaîne = ExtraitChaîne(sImg1,1,"content-type",DepuisDébut)
fLogger.InfoMsg(ChaîneConstruit("ATTACHMENTS sContentTypeImg1 : %1",sContentTypeImg1))
sContentDispositionImg1 est une chaîne = ExtraitChaîne(sImg1,1,"content-disposition",DepuisDébut)
fLogger.InfoMsg(ChaîneConstruit("ATTACHMENTS sContentDispositionImg1 : %1",sContentDispositionImg1))



J'arrive bien à récupérer l'entête générale dans la variable sContentTypeGlobal ainsi que la boundary dans la variable sBoundary.
Par contre, la récupération du buffer via WebserviceParamètre(paramBuffer) n'abouti pas. Les étapes suivantes ne sont pas traitées.
Comme si le service était bloqué. L'alternative que vous proposiez en utilisant un paramètre de type buffer dans les arguments de la procédure nécessiterait de modifier l'appel du service dans l'apk flutter et cela n'est pas possible.

Merci pour votre aide,
Miembro registrado
397 mensajes
Popularité : +13 (13 votes)
Publicado el 16,abril 2024 - 10:00
Bonjour,

Comme quoi il est toujours bon de tester, et comme quoi on en apprend tous les jours....

Après quelques tests, il semblerait que dans ce cas précis (upload de fichiers classique sur un point d'entrée de webservice REST), WebDev détecte que la requête contient des fichiers uploadés, et les traite déjà en interne avant d'envoyer le contenu au code du point d'entrée. Il détecte aussi la boundary tout seul et met les différents contenus dans différents paramètres.

C'est pourquoi WebserviceParamètre(paramBuffer) ne fonctionne pas, car le contenu de la requête est traité en amont et est donc "vide" au moment d'arriver au code du projet.

Un code qui fonctionne (et testé cette fois) :
i est un entier = 1
bufParam est un Buffer = WebserviceParamètre(i)

TANTQUE bufParam <> ""
Trace(bufParam)
// file=dummy.pdf;C:\WEBDEV 28\Personnel\TmpUpload\WEBDEV_0_13000_-2100725022_31100882.upl

i++
bufParam = WebserviceParamètre(i)
FIN

RENVOYER Vrai


Note : le format d'entrée du point d'entrée doit être sur "Variable" ou "Formulaire HTML". J'ai testé le code en v28, pas en v2024

Pour chaque fichier uploadé, vous allez recevoir dans bufParam une chaîne contenant : le nom du fichier, puis après un point-virgule, le chemin du fichier temporaire sur le serveur du fichier uploadé. Il suffira de copier et renommer ce fichier à l'endroit où vous le souhaitez.

Il est assez particulier que WebDev ne permette pas, même en indiquant le format le plus souple d'entrée ("Variable"), de réellement récupérer le contenu sans aucun traitement en amont. Il serait peut-être pertinent de le signaler au support technique pour éventuellement une amélioration dans les prochaines versions. :)
Miembro registrado
16 mensajes
Publicado el 16,abril 2024 - 10:27
Bonjour,

Merci beaucoup pour ses explications très claires.
J'ai bien le format du point d'entrée en "Formulaire HTML" J'utilise WD28.
J'ai pris le morceau de code que vous m'avez envoyer. Par contre de mon côté bufParam est vide. J'ai 2 images qui me sont envoyées mais Taille(bufParam) = 0. Du coup je ne rentre pas dans la boucle du TANTQUE...

Il y a encore un mystère Windev à perser.
Miembro registrado
16 mensajes
Publicado el 16,abril 2024 - 10:46
J'ai essayé ceci mais ça ne change rien :

bufParam est un Buffer = WebserviceParamètre("")

Taille(bufParam- vaut toujours 0.
Miembro registrado
16 mensajes
Publicado el 16,abril 2024 - 10:54
En mettant le format du point d'entrée à "Variable" cela n'a rien changé non plus.
Miembro registrado
16 mensajes
Publicado el 18,abril 2024 - 08:20
Bonjour,

Quelqu'un aurait une idée. Je suis dans une impasse.

Merci de votre aide.
Miembro registrado
16 mensajes
Publicado el 18,abril 2024 - 10:34
Bonjour,

Merci beaucoup bchanudet pour ton aide. J'ai réussi à sauvegarder une image.
Cela fonctionne quand j'utilise Postman. Le buffer contient bien les infos de l'image et le déplacement et renommage me permet bien de pouvoir afficher l'image transmise.

Seul hic c'est qu'il semble qu'avec les infos fournies par la méthode Flutter (via l'apk) cela ne fonctionne pas de la même manière. bufParam reste désespérément vide 😒

Je continue de chercher...