IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Delphi Discussion :

Impossible de convertir l'entrée JSON en flux


Sujet :

Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    Webmaster
    Inscrit en
    Février 2017
    Messages
    43
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Congo-Brazzaville

    Informations professionnelles :
    Activité : Webmaster

    Informations forums :
    Inscription : Février 2017
    Messages : 43
    Par défaut Impossible de convertir l'entrée JSON en flux
    Bonjour ou bonsoir ! Comme l'indique le titre, j'ai cette erreur et google ne m'aide pas beaucoup. Avant tout, je voudrais d'abord expliquer ce que je veux faire :

    Je veux télécharger une base de données de backup SQLite (.db) qui se trouve sur le serveur (php). Pour cela j'utilise le composant TRESTClient parce que j'y attache un JSON pour faire quelques vérifications avant de valider le téléchargement. Du côté serveur (après tout vérification), je récupère le fichier .db, constitue un tableau d'octet puis renvoie un JSON à mon client delphi.
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    $myDb = 'c:/baseDeDonneesBackup.db'; // Le chemin du fichier est correct.
    $labase = file_get_contents($myDb);
        for ($i=0; $i < strlen($labase); $i++) { 
            $tab[$i] = mb_ord($labase[$i], "UTF-8");
        }
        echo  json_encode(["status" => 1, "base" => $tab],JSON_UNESCAPED_UNICODE);

    A partir de ce code php j'obtient un tableau similaire [83,81,76,105,116,101,32,102,111,114,109...].

    Du côté de mon client Delphi, je tente d'enregistrer ce tableau (le contenu) dans un fichier en local, et pour cela je procède de la manière suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
     try
        RESTRequest.Execute();
        reponseSever := TJSONObject.ParseJSONValue(RESTResponse.JSONText);
        resultat := True;
        traiterReponseServer(reponseSever); // Le traitement se fait dans cette fonction
      except
        on e: Exception do
        begin
          resultat := False;
        end;
     
     end;
    Fonction de traitement du résultat

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    procedure TForm1.traiterReponseServer(contenuJson: TJSONValue);
    var
      fileName: string;
      fs : TMemoryStream;
      aJSON: TJSONArray;
      mon_stream : TStream;
    begin
     
      fileName := TPath.Combine( Tpath.Combine(Tpath.GetDocumentsPath, 'testerFichier'), 'base.db') ;
     
      mon_stream := TStream.Create;
      aJSON := contenuJson.GetValue<TJSONArray>('info');
     
      mon_stream := TDBXJSONTools.JSONToStream(aJSON); // Le problème est ici.
     
      fs := TMemoryStream.Create;
      fs.LoadFromStream(mon_stream);
      fs.SaveToFile(fileName);
     
    end;
    Normalement (selon moi), je m'attendais à recevoir mon fichier tout tranquillement, mais j'ai une erreur TDBXError avec le message "Impossible de convertir l'entrer JSON en flux". Je me demande bien là où j'ai fait mal fait les choses.

    PS : j'ai privilégié TRESTClient pour deux choses principalement : 1. Pas de problème de TLS (Requête en https); 2. J'envoie assez facilement mon JSON.
    Mais s'il y a des suggestions sur une meilleur façon de réaliser ce que je veux faire, je suis preneur

  2. #2
    Membre Expert
    Avatar de pprem
    Homme Profil pro
    MVP Embarcadero - formateur&développeur Delphi, PHP et JS
    Inscrit en
    Juin 2013
    Messages
    1 876
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : MVP Embarcadero - formateur&développeur Delphi, PHP et JS
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2013
    Messages : 1 876
    Par défaut
    mais quelle horreur (cri du coeur) : utiliser du JSON pour transférer du binaire, quelle drôle d'idée.

    et sinon, au lieu de 'info', tu devrais peut-être mettre 'base' dans Delphi, histoire d'avoir la même propriété.

    JSON pour utiliser TRESTRequest, ok, pourquoi pas, mais dans ce cas un simple POST ou GET ferait très bien l'affaire dans le code.

    Tu peux même utiliser ou t'inspirer ce que j'ai mis sur https://github.com/DeveloppeurPascal...u_download.pas pour télécharger ton fichier que côté PHP tu peux envoyer par un simple readfile().

  3. #3
    Membre averti
    Homme Profil pro
    Webmaster
    Inscrit en
    Février 2017
    Messages
    43
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Congo-Brazzaville

    Informations professionnelles :
    Activité : Webmaster

    Informations forums :
    Inscription : Février 2017
    Messages : 43
    Par défaut
    Citation Envoyé par pprem Voir le message
    mais quelle horreur (cri du coeur) : utiliser du JSON pour transférer du binaire, quelle drôle d'idée.
    Je m'attendais tellement à cette réaction

    J'avais vu votre tutoriel et d'ailleurs je l'utilisais, mais je recontre deux souci de ce côté : le premier, je n'arrive pas à envoyer un JSON (pour des vérications côté serveur) avec le composant THTTPClient et le deuxième est que ça n'accepte pas les requêtes en HTTPS avec comme message d'erreur certificat serveur non valide ou absent et là aussi google ne m'aide pas trop pour resoudre ce problème.

    Alors, sur le dernier point je ne sais pas si le problème vient du serveur (xamp), mais j'ai de gros doutes, ou si le problème vient du composant, je suis plutôt de cet avis, parce que le composant TRESTClient transmet bien les requêtes HTTPS et d'ailleurs dans mes recherches, j'ai lu des truc un flou comme quoi TRESTClient se base sur l'api du système pour la gestion du HTTPS et que l'autre ce n'était pas le cas ... D'où l'hérésie d'utiliser du JSON pour le téléchagement.

  4. #4
    Membre Expert
    Avatar de pprem
    Homme Profil pro
    MVP Embarcadero - formateur&développeur Delphi, PHP et JS
    Inscrit en
    Juin 2013
    Messages
    1 876
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : MVP Embarcadero - formateur&développeur Delphi, PHP et JS
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2013
    Messages : 1 876
    Par défaut
    En fait il y a deux choses : JSON n'est pas fait pour ça à la base, même si on peut le faire, mais c'est vraiment pas le but du truc. Si le code que tu as copié collé ici est exactement celui que tu utilises, tu as la solution dans ma précédente réponse.

    L'autre chose, c'est le certificat SSL associé à ton url. S'il est rejeté par THTTPClient, il devrait aussi l'être par TRESTClient sauf que selon les versions de Delphi ça ne fait pas les mêmes contrôles, contrôles effectués par le système d'exploitation qui rejette désormais les certificats autogérés.

    Pour résoudre ce soucis (qui en est un qui te bloquera totalement à un moment ou un autre) tu as trois solutions :
    - Trouver comment générer un certificat valide pour ton serveur (par exemple en utilisant Let's Encrypt ou ZeroSSL. La solution se trouvera probablement en mettant "XAMP ssl certificate how to" ou un équivalent dans tes recherches.
    - Faire en sorte de passer par le http au lieu du https (pas une bonne idée, mais efficace)
    - Enfin, si le certificat SSL du https est autogéré, tu peux aussi enregistrer le certificat dans l'ordinateur client et par conséquent il sera accepté parce que connu des deux côtés, mais ça implique de manipuler les ordinateurs qui utiliseront ton logiciel. pas top du tout.

  5. #5
    Membre averti
    Homme Profil pro
    Webmaster
    Inscrit en
    Février 2017
    Messages
    43
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Congo-Brazzaville

    Informations professionnelles :
    Activité : Webmaster

    Informations forums :
    Inscription : Février 2017
    Messages : 43
    Par défaut
    Citation Envoyé par pprem Voir le message
    - Faire en sorte de passer par le http au lieu du https (pas une bonne idée, mais efficace).
    J'opte finalement pour cette solution, mais je voudrais signifier que le problème vient en effet du serveur XAMP parce que j'ai mis mon application php sur un serveur gratuit pour voir et les requêtes HTTPS marchent sans problème, cependant j'ai mis un try-catch pour switcher du HTTPS en HTTP en cas de problème de certificat.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    use StrUtils
    ...
    try
         serveur_reponse := serveur.Get(from_url);
    except on e: Exception do
         serveur_reponse := serveur.Get( ReplaceStr(from_url, 'https', 'http') );
    end;

  6. #6
    Expert éminent
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 56
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Par défaut
    1) il serait plus judicieux de renvoyer directement la base

    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    <?php
      Header('Status: 1');
      Header('Content-Type: application/octet');
      readfile('c:/baseDeDonneesBackup.db');

    2) quitte à faire du JSON, autant mettre la base dans une chaine en base64, c'est la méthode traditionnelle pour transmettre du binaire en JSON

    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    <?php
     
      echo  json_encode(["status" => 1, "base" => base64_encode(file_get_contents('c:/baseDeDonneesBackup.db'))],JSON_UNESCAPED_UNICODE);
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  7. #7
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 090
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 090
    Par défaut
    Pour ton autre version en tableau de Byte, en théorie, je constate que cette méthode est fréquente en ligne sur Developpez.net et StackOverflow, curieuse méthode, volumétrie infâme

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
       mon_stream := TStream.Create;
    inutile et faux, c'est JSONToStream qui va créer une instance, ce n'est pas à toi de savoir quoi, cela peut changer à l'avenir, il faut le manipuler collle un TStream générique qui actuellement est instancié comem un TBytesStream, un TStream est une classe abstraite, cela ne doit jamais être instancié, étonnant même que tu n'es pas de warning de compilation

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
      aJSON := contenuJson.GetValue<TJSONArray>('base'); // 'base' et non 'info'
     
      // Vérifie que aJSON[0] est un TJSONNumber, c'est la cause de l'exception
     
      mon_stream := TDBXJSONTools.JSONToStream(aJSON); // Crée un TStream en analysant les [xx, ww, zz] vers un TBytes temporaire qui lui sert à remplir le Stream, c'est lourd, pourquoi ne pas renvoyer le TBytes pour laisser choisir le type de Stream
      try
        with TFileStream.Create(fileName, fmCreate) do
        try
          // mon_stream.Seek(0, soBeginning); // en théorie non nécessaire
          CopyFrom(mon_stream, mon_stream.Size); 
        finally
          Free();
        end;
      finally
        mon_stream.Free();
      end;
    end;
    D'ailleurs en PHP l'encodage "UTF-8" est peut-être une erreur car si mb_ord envoi un nombre au delà de 255 cela plantera aussi, il faut traiter la chaine comme un binaire pur aussi en PHP
    Si tu as un binaire qui contient ce qui ressemble à un caracètère non ANSI mb_ord va le traduire en char unicode
    la méthode d'envoie PHP contient un possible erreur, il faut gérer les char côté php comme des Byte par comme des chars, j'utilise plutôt la foncton fread


    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    $myDb = 'c:/baseDeDonneesBackup.db'; // Le chemin du fichier est correct.
     
     
    $labasesize = filesize($myDb);
    $labase = fopen($myDb, 'rb');
    $contents= fread($labase , $labasesize );
    $tab= unpack("C*",$contents); // si tu trouve le bon format pour tout simplement transformer la chaine en array of byte, langage de merde, le non-typage je déteste !
    fclose($labase );
     
    /* a tenter aussi plus court
    $labase = file_get_contents($myDb);
    $tab= unpack("C*", $labase ); 
     
    */
     
        echo  json_encode(["status" => 1, "base" => $tab],JSON_UNESCAPED_UNICODE);

    il est tout de même plus simple de lire le fichier en binaire et l'exploiter directement que de le charger dans une chaine et tenté un ré-encodage approximatif ! non ?
    C'est vraiment dégueux le php, tout passe par des chaines même fread, comme lire du binaire peut-être aussi compliqué ?
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  8. #8
    Expert éminent
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 56
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    C'est vraiment dégueux le php, tout passe par des chaines même fread, comme lire du binaire peut-être aussi compliqué ?
    en fait c'est exactement ce que faisait Delphi jusque 2009, un string était un tableau d'octets dynamiques, un peu comme TBytes mais avec toutes les fonctions utiles comme Pos() Delete() Insert()...et il a fallut attendre longtemps pour les avoir sur TArray<T> et donc TBytes...par contre, il reste toujours plus facile d'utiliser Pos pour trouver un délimiteur ASCII (voir binaire avec la notation #13#10 par exemple) dans un flux binaire à travers un RawByteString, même si c'est totalement non recommandé.

    et donc en PHP ça passe comme avant Delphi 2009 c'est mbstring qui sert pour l'unicode avec mb_strlen() au lieu de strlen() par exemple.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  9. #9
    Membre averti
    Homme Profil pro
    Webmaster
    Inscrit en
    Février 2017
    Messages
    43
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Congo-Brazzaville

    Informations professionnelles :
    Activité : Webmaster

    Informations forums :
    Inscription : Février 2017
    Messages : 43
    Par défaut
    En vu de la richesse des réponses, après plusieurs testes, les deux méthodes que j'avais entrepris marchent. Pour la première méthode (celle du poste) qui consiste à avoir un tableau d'octet, la phrase ci-dessous dit tout.

    Citation Envoyé par ShaiLeTroll Voir le message
    Mettre le contenu d'un fichier .DB dans une UnicodeString est un non sens, c'est un binaire non encodé que vous tentez de faire décoder.
    si vous n'aimez pas les Stream, un TBytes fera aussi très l'affaire.
    Pour cela j'ai modifier mon code php en :

    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    $myDb = 'c:/baseDeDonneesBackup.db'; // Le chemin du fichier est correct.
    $labase = file_get_contents($myDb);
     
     for ($i=0; $i < strlen($labase); $i++) { 
            $tab[$i] = ord($labase[$i]); // En lieu et place d'utiliser "mb_ord" qui converti les entrés en unicode, j'utilise "ord"
        }
     
     echo  json_encode(["status" => 1, "base" => $tab],JSON_UNESCAPED_UNICODE);

    Côté delphi, c'est ceci

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    fileName := TPath.Combine( Tpath.Combine(Tpath.GetDocumentsPath, 'testerFichier'), 'base.db') ;
      // mon_stream := TStream.Create; // A ne surtout pas faire.
      aJSON := contenuJson.GetValue<TJSONArray>('base');
      mon_stream := TDBXJSONTools.JSONToStream(aJSON); 
      try
        with TFileStream.Create(fileName, fmCreate) do
        try
          // mon_stream.Seek(0, soBeginning); // en théorie non nécessaire
          CopyFrom(mon_stream, mon_stream.Size);
        finally
          Free();
        end;
      finally
        mon_stream.Free();
      end;
    Pour la deuxième méthode, celle qui consiste à encoder le contenu en base64, code php :

    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    echo  json_encode(["status" => 1, "base" => base64_encode(file_get_contents('c:/baseDeDonneesBackup.db'))],JSON_UNESCAPED_UNICODE);

    Côté delphi
    Citation Envoyé par ShaiLeTroll Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    fileName := TPath.Combine( Tpath.Combine(Tpath.GetDocumentsPath, 'testerFichier'), 'base.db') ;
    contenu := reponseServer.GetValue<String>('base');
    with TFileStream.Create(fileName, fmCreate) do
    try
      Buffer := TNetEncoding.Base64.DecodeStringToBytes(contenu);
      WriteBuffer(Buffer, Length(Buffer));
    finally
      Free();
    end;
    Citation Envoyé par ShaiLeTroll Voir le message
    Par économie de mémoire, je mettrais contenu comme TBytes, j'ignore si le TJSONValue sait gérer ce type de cette façon mais il a l'avantage de ne pas chercher à décoder le buffer base64 qui est par définition une chaine ASCII (7 bits utilisé par Char, le 8ème à zéro)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    contenu := reponseServer.GetValue<TBytes>('base');
    Pour le TBytes, j'ai eu une erreur TJSONString ne peut pas être converti en System.Array<TBytes> (si je ne dis pas de bétise). Mais je tiens à souligner que j'ai eu cette erreur en ayant fait ça côté serveur :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    $labase = file_get_contents($myDb);
    $tab= unpack("C*", $labase ); 
    echo  json_encode(["status" => 1, "base" => $tab],JSON_UNESCAPED_UNICODE);

    et ceci côté delphi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    var tBytes : TBytes
    begin
    tBytes = reponseServeur.GetValue<TBytes>('base'); // C'est ici que j'ai eu l'erreur
    end;

    Citation Envoyé par ShaiLeTroll Voir le message
    il est tout de même plus simple de lire le fichier en binaire et l'exploiter directement que de le charger dans une chaine et tenté un ré-encodage approximatif ! non ?
    C'est vraiment dégueux le php, tout passe par des chaines même fread, comme lire du binaire peut-être aussi compliqué ?
    C'est vrai que c'est un peu plus simple et plus économe, mais le souci est que je reçois un objet json de ce genre {"status":1,"base":{"1":83,"2":81,"3":76,"4":105,"5":116...} après je ne sais pas s'il faut le récupérer comme un string (je crois d'ailleurs que c'est à l'origine de mon erreur TJSONString) ou itérer sur chaque valeur ("1","2".) pour récupérer les octets et de les écrire un à un dans un fichier ? Le vrai soucis c'est comment le traiter.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 10/08/2007, 11h57
  2. Réponses: 2
    Dernier message: 19/05/2007, 20h51
  3. Impossible de convertir une base 2003 vers 2000
    Par soso78 dans le forum Access
    Réponses: 4
    Dernier message: 07/01/2007, 11h04
  4. Impossible de convertir les sauts de lignes \n en <br> dans une jsp.
    Par michaelcourcy dans le forum Servlets/JSP
    Réponses: 1
    Dernier message: 28/11/2006, 17h32
  5. [XPATH] Impossible de convertir #BOOLEAN en un NodeList
    Par claudyyyyy dans le forum Format d'échange (XML, JSON...)
    Réponses: 7
    Dernier message: 08/02/2006, 16h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo