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

  1. ###raw>post.musername###
    Membre à l'essai
    Rest SDK récupérer un fichier dans le body et le stocker dans un dossier
    Bonjour à tous,

    Voilà, j'expose le contexte avec ce que j'ai fait, et ce que j'ai pu faire, et le problème sur lequel je bloque depuis quelque temps.

    Je suis en train de développer un serveur, un webservice en C++ avec la librairie de microsoft C++ Rest SDK.
    J'ai pu setup mon serveur pour prendre en compte les requêtes HTTP en POST comme 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
    17
    18
    19
     
    int main(int argc, const char** argv)
    {
         http_listener listenerUpload(L"http://localhost/upload");
     
        try
        {
              listenerUpload
    			.open()
    			.then([&listenerUpload]() {std::TRACE(L"\nServer started\n"); })
    			.wait();
     
              while (true);
        }
        catch (std::exception const & e)
        {
    	std::cout << e.what() << std::endl;
        }
    }


    Je récupère ma requête via ma méthode:

    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
     
     
    void handle_uploadPostFile(http_request request)
    {
       is_authorized(request); //Cette methode sert à vérififier le token, sa validité, sa date d'expiration et les roles de l'utilisateur
     
       auto file = request.body(); //Je veux  récupérer le fichier envoyé ici
     
       boost::filesystem::path path(boost::filesystem::current_path()); //Je crée l'adresse de mon répertoire ou je veux stocker mes fichiers reçu 
       std::string storageFolder{ "\\media" };
       std::string repository{ path.string() + storageFolder };
     
       std::ofstream filePath(repository);
       filePath << file;
     
       request.reply(200, file);   
     
    }


    Le retour de ma requête "request.reply(200, file);" me retourne ça quand je lui passe par exemple une image:



    Je voudrais stocker ce fichier dans un dossier, je vous ai épargné tout les morceaux de code en commentaire après mes maintes tests.
    En espérant que l'on puisse m'aider à résoudre enfin ce problème sur lequel je bute depuis un moment.
    Merci d'avoir pris le temps de me lire et m'aider à me sortir de ce problème.
      0  0

  2. #2
    Rédacteur/Modérateur

    C'est quoi le problème ?
    Tu reçois le fichier, crées le path, ensuite il faut juste ouvrir un fichier en écriture et y copier les données reçues.
    Quand au retour, je ne comprends pas pourquoi tu renvoies les données de l'image.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Membre à l'essai
    Ah oui, j'ai oublié de préciser, je renvoie les données de l'image pour tester que tout fonctionne, je m'en sers comme debug pour le moment.

    Tu reçois le fichier: ça c'est OK;
    crées le path: ça c'est OK;

    ensuite il faut juste ouvrir un fichier en écriture et y copier les données reçues: C'est là que je me suis un peu perdu ,je ne suis pas non plus expert en C++, j'avais la même méthode en tête mais je ne vois pas trop comment faire, l'objet "file" est de type concurrency::streams::istream j'ai donc des problèmes de convertion quand je veux crée un fichier avec les données du fichier reçu.

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
     
    std::ofstream filePath(repository); //J'ai oublié de le rajouter dans le premier commentaire je le rajoute ici
     
    filePath << file; //là j'ai essayé d'insérer mon fichier dans mon dossier, mais en vérité c'est juste les données de mon fichier reçu


    Je vois pas trop comment je pourrais m'y prendre, à vrai dire c'est peut être à force de tourner en rond..

  4. #4
    Membre à l'essai
    Ce que je ne comprend pas, c'est pourquoi il me retourne 1 comme valeur si je fais: std::cout << file << std::endl;

  5. ###raw>post.musername###
    Rédacteur/Modérateur
    std::ofstream filePath(repository); va ouvrir un stream vers.. un dossier ? Ça me parait difficilement possible. Faut faire preuve d'un peu de logique
    Un stream doit s'ouvrir vers un fichier.
    Au hasard
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    const std::string filepath(repository + "\\toto.png");
    std::ofstream file(filepath, std::ios_base::openmode::binary);
    if(file.is_open())
      file << filedata;
    else
      // error


    Ce que je ne comprend pas, c'est pourquoi il me retourne 1 comme valeur si je fais: std::cout << file << std::endl;
    file est de la donnée brute, cout est fait pour afficher du texte.
      1  0

  6. #6
    Membre à l'essai
    Merci pour ton retour Bousk.

    Pour "std::ofstream file(filepath, std::ios_base::binary);" apparemment std::ios_base::binary suffit.

    Par contre :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
     
    file << filedata;


    Ne fonctionne pas, le rendu du fichier donne ça:



    Une idée ?
    Pour moi, le problème viendrait qu'il prend le nom du fichier et le content-type au lieu de passer seulement le contenu du fichier ?

    Comme ça:


  7. #7
    Rédacteur/Modérateur

    request.body(); te retourne le contenu de la requête HTTP.
    Bien sûr une requête HTTP contient plein de conneries, des en-têtes etc.
    Toi ce qu'il te faut c'est bien entendu juste le contenu du fichier. Qu'il te faut extraire du body si tu n'as que ça.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  8. #8
    Membre à l'essai
    Je me demande, si cette librairie n'accepte pas que les data au format json.
    La doc là dessus n'est pas très claire, et j'ai pu voir une personne qui disait que la data devait être du json, les seul autres moyens d'extraires les data sont "extract_string()", "extract_utf8string()" et "extract_utf16string()".

    Un peu comme ce post sur stack overflow: https://stackoverflow.com/questions/...est-sdk-casabl

  9. #9
    Membre à l'essai
    J'ai ajouté 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
    17
    18
    19
    20
    21
    22
    23
     
     
    try
    {
    	auto stream = concurrency::streams::fstream::open_ostream(
    		U("data2.txt"),
    		std::ios_base::out | std::ios_base::binary).then([request, fileBuffer](pplx::task<Concurrency::streams::basic_ostream<uint8_t>> Previous_task)
    	{
     
    		*fileBuffer = Previous_task.get();
    		request.body().read_to_end(Previous_task.get().streambuf());
    		//request.body()["file"];
     
    	}).then([=]()
    	{
    		fileBuffer->close();
    	});
     
    }
    catch (std::exception const & e)
    {
    	std::cout << e.what() << std::endl;
    }


    J'ai réussis à récupérer tout dans un fichier texte, là en l'occurrence data2.txt pour tester.
    Par contre je ne sais pas comment récupérer juste le fichier passer dans le body sans récupérer tout le résultat du body, j'ai essayé "request.body()["file"];" mais pour le coup le résultat est vide..

    Voilà le résultat dans data2.txt:



    Une idée ?

  10. #10
    Rédacteur/Modérateur

    Ça fait un moment que j'ai plus touché à cette lib, mais est-ce que extract_vector ne serait pas mieux ??
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  11. #11
    Expert éminent sénior
    Ça, c'est un corps de requête constitué de données de formulaire.
    Ce que tu veux, c'est isoler le fichier, c'est-à-dire la valeur du champ "file". Avec un peu de chance, ton framework en est capable.

    Dans le cas contraire, il faudra faire ça manuellement!
    • Tout d'abord il faudra lire les headers pour y récupérer la séquence qui sépare les champs (en gros, il y a un header qui te dit que le délimiteur est ----------------------------058424172275921691034755)
    • Puis, lire les headers des champs eux-mêmes, notamment le header Content-Disposition, pour trouver le champ avec le bon nom.
    • Une fois que tu as les limites du champ, et la fin de ses headers (la ligne vide, donc séquence "\r\n\r\n"), tout ce qui est entre "fin des headers" et "fin du champ" est le fichier!
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  12. #12
    Membre à l'essai
    Merci pour vos réponses.

    Médinoc, je vois ce que tu veux dire, tu aurais un exemple pour récupérer le délimiteur, et seulement le délimiteur ?

    Je le récupère comme ça, le premier est un message de debug, le second le content-type, je ne sais pas trop encore comment récupérer le boundary, ou si tu parlais de tout récupérer comme ceci:


    J'ai aussi une autre petite question, peut-être bête en soit, mais j'ai testé de copier coller dans un fichier texte (.txt), le texte, le contenu de mon image, et ensuite de changer l'extension en .png, mais cela ne fonctionne pas en tant qu'image, l'image ne s'affiche pas, ça me donne ça comme résultat, lorsque je l'ouvre:

  13. #13
    Expert éminent sénior
    Il faut séparer les champs du Content-Disposition selon le ; (point-virgule suivi d'un espace) (idéalement il faudrait faire un code capable de séparer SAUF quand c'est entre guillemets), puis trouver le premier champ qui commence par boundary=, et sauter ses 9 premiers caractères.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  14. #14
    Membre à l'essai
    J'ai vu que tu as répondu pendant que j'étais en train de ré-éditer mon ancien message, donc je le repose ici.

    J'ai aussi une autre petite question, peut-être bête en soit, mais j'ai testé de copier coller dans un fichier texte (.txt), le texte, le contenu de mon image, et ensuite de changer l'extension en .png, mais cela ne fonctionne pas en tant qu'image, l'image ne s'affiche pas, ça me donne ça comme résultat, lorsque je l'ouvre:


    Aurais-tu une idée ?


    Il faut séparer les champs du Content-Disposition selon le ; (point-virgule suivi d'un espace) (idéalement il faudrait faire un code capable de séparer SAUF quand c'est entre guillemets), puis trouver le premier champ qui commence par boundary=, et sauter ses 9 premiers caractères.
    Ahlala, ce n'est pas aussi simple qu'en PHP et Javascriptet autres langages de haut niveau
    Peut-être que la libraire boost propose quelque chose pour ça.

  15. #15
    Expert éminent sénior
    Citation Envoyé par eristoff72 Voir le message
    J'ai aussi une autre petite question, peut-être bête en soit, mais j'ai testé de copier coller dans un fichier texte (.txt), le texte, le contenu de mon image, et ensuite de changer l'extension en .png, mais cela ne fonctionne pas en tant qu'image, l'image ne s'affiche pas, ça me donne ça comme résultat, lorsque je l'ouvre:
    (snip screenshot)
    Ça aurait potentiellement eu des chances de passer avec Notepad++ (et encore), mais ça ne m'étonne pas que ça ait échoué, compte-tenu que le format PNG contient des retours à la ligne de plusieurs types, des octets nuls, etc. Du pur "fichier binaire".

    Ahlala, ce n'est pas aussi simple qu'en PHP et Javascriptet autres langages de haut niveau
    Ouaip. Je suis bien placé pour le savoir, j'ai eu récemment à ré-implémenter ça en C# pour avoir un serveur 100% en code managé (alors que les classes du Framework .Net sont des wrappers vers un service non-managé de Windows).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  16. #16
    Membre à l'essai

    Ouaip. Je suis bien placé pour le savoir, j'ai eu récemment à ré-implémenter ça en C# pour avoir un serveur 100% en code managé (alors que les classes du Framework .Net sont des wrappers vers un service non-managé de Windows).
    En C# t'as déjà les méthodes natif qui le font, je crois bien.
    C'est pour ça que tu as la logique sans la technique de comment le faire en C++ .

    Je continue de regarder, mais si quelqu'un sait, je suis preneur, j'ai un peu de mal avec la fatigue, je me noie dans le code lol

    Ah oui et du coup je me demandais, si je rebuild le fichier avec le continue de mon image, là contrairement à mon copier coller cela devrait fonctionner ?

  17. #17
    Rédacteur/Modérateur

    Ben la solution est claire : tu es en train d'implémenter un serveur web, il te faut implémenter le traitement des requêtes.
    HTTP Rest SDK c'est juste la brique super basique pour récupérer une requête. Si tu veux pouvoir interpréter des requêtes plus complexes, faut complexifier ton serveur.
    Lire les headers pour trouver les boundaries de chaque élément est un début. Mais il va falloir surtout que tu lises les normes et attentes d'un tel truc.
    C'est la différence entre j'ai un socket connecté pour envoyer/recevoir hello world et j'ai un protocol permettant d'échanger des données.
    Tu as une lib qui permet de recevoir des requêtes web, elle fournit de quoi extraire les headers et le body, mais il faut que tu ajoutes tout ce qu'il manque.

    Et non copier coller de je ne sais où aura aucune chance de sortir un PNG valide. Tu as énormément de données dedans qui ne sont pas représentable avec des caractères. La seule chance c'est d'extraire ça via extract_vector et l'insérer dans un fichier ouvert en écriture binaire.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  18. ###raw>post.musername###
    Membre à l'essai
    extract_vector() lui me retourne rien on dirait bien.

    La seule chance c'est d'extraire ça via extract_vector et l'insérer dans un fichier ouvert en écriture binaire.
    C'est justement ça que j'essaye de faire là

    Bon, ce que j'ai pu voir, finalement il ne me retourne pas rien, mais il me retourne la même chose que le contenant de mon fichier généré soit ça:



    Je vois pas comment je peux extraire la partie du fichier pour le re-générer, c'est bien ce que me retourne "extract_vector()"

    Il faut séparer les champs du Content-Disposition selon le ; (point-virgule suivi d'un espace) (idéalement il faudrait faire un code capable de séparer SAUF quand c'est entre guillemets), puis trouver le premier champ qui commence par boundary=, et sauter ses 9 premiers caractères.
    J'ai une question, là dans mon code, une fois que j'ai trouvé "boundary=", ce qu'il y a après "=", je le prend en compte dans l'écriture de mon fichier ?
    Et ça:
    Content-Disposition: form-data; name="file"; filename="2.png"
    Content-Type: image/png
    Je le prends en compte également ?

    En gros je prend tout ça :

    Content-Disposition: form-data; name="file"; filename="2.png"
    Content-Type: image/png

    blabla con tenu du fichier:

    ----------------------------379412310113082987734398
    Content-Disposition: form-data; name="file"; filename="2.png"
    Content-Type: image/png

    ‰PNG
    

    IHDR Å } \Mï sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d 0IDATx^íÁkG‡}Ìс@rÌÿ*Ó Œ<—qbaã‘ ’L&AA‘uPŒû0F
    Âm+³³((dÇ…`fa6Q´ŠÀ‹‚«%b&ƒµ,{˜›î›öU½êîêîšQ÷¸4VÏü¾ƒ]SÕ]ÕÝRªêê©wî €
    àS °|
    v€O Àð) Ø> ;À§ `ø ì Ÿ €àS °|
    blablabla le reste du contenu ...
    ----------------------------379412310113082987734398--
    Parce que ça je l'ai déjà, c'est ça que j'ai dans mon fichier.

    ou il me faut juste :

    ‰PNG
    

    IHDR Å } \Mï sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d 0IDATx^íÁkG‡}Ìс@rÌÿ Ó Œ<—qbaã‘ ’L&AA‘uPŒû0F
    Âm+³³((dÇ…`fa6Q´ŠÀ‹‚«%b&ƒµ,{˜›î›öU½êîêîšQ÷¸4VÏü¾ƒ]SÕ]ÕÝRªêê©wî €
    àS °|
    v€O Àð) Ø> ;À§ `ø ì Ÿ €àS °|
    blablabla le reste du contenu ...
    Bon, pour supprimer les lignes j'ai voulu utiliser cette méthode:

    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
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
     
     
    std::regex expressionContent ("[-]+[0-9]+ Content-Disposition: form-data; name=\".+\"; filename=\".+\" Content-Type: .+ ");
     
    	for (int i = 0; i < fileBody.size(); i++)
    	{
    		file << fileBody[i];
    	}
     
            auto exPath = path.string() + "\\bufferFile.txt";
    	std::ifstream filetemp(exPath);
    	std::cout << exPath << std::endl;
    	std::cout << "toto2" << std::endl;
    	char str[255];
     
    	if (filetemp.good())
    	{
    		while (getline(filetemp, line))
    		{
    			bool match = std::regex_match(line, expressionContent);
     
    			if (match)
    			{
    				std::cout << match << std::endl;
    				std::cout << "if" << std::endl;
    				secondfile << line;
     
    			}
    			else
    			{
    				std::cout << match << std::endl;
    				std::cout << "else" << std::endl;
    				std::cout << line << std::endl;
    			}
     
    		}
     
    	}


    Le problème étant que qu'il prend en compte mes regex, mais pas le reste, et dans ma regex il prend également "‰PNG" et les "0" en compte ce qui foire tout.
    Vous avez pas une idée ?

    J'ai essayé cela:

    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
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
     
    auto content_type = utility::conversions::to_utf8string(request.headers()[header_names::content_type]);
     
    auto exPath = path.string() + "\\bufferFile.txt";
    std::ifstream filetemp(exPath);
     
    if (filetemp.good())
    {
     
    long i;
    for ( i = 0; getline(filetemp, line); ++i)
    		{
     
    			//std::cout << line << std::endl;
     
    			if (std::regex_match(line.begin(), line.end(), expressionContent))
    			{
    				std::cout << line << std::endl;
    				secondfile << line;
     
    			}
     
    			if (line != content_type)
    			{
    				std::cout << line << std::endl;
    				secondfile << line;
     
    			}
     
    		}
    std::cout << "number of lines: " << i << std::endl;
    }
     
    std::stringstream ss;
    ss << secondfile.rdbuf();
    request.reply(200, ss.str());


    Mais le problème, c'est qu'il m'affiche jamais le contenu du fichier et seulement

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    ----------------------------840549026523147999060228
    Content-Disposition: form-data; name="file"; filename="2.png"
    Content-Type: image/png
     
    ëPNG


    J'ai bataillé tout le week end mais je n'ai pas réussi à me débloquer là dessus..
    Je me demandais peut être qu'il peut pas récupérer le contenu d'un fichier dans un fichier texte, mais je l'ai déjà fait donc...

    Quelque soit les cas, soit il m'affiche rien, soit il m'affiche le résultat ci-dessus.

    Le retour sur postman que j'ai:



    Ce que je ne comprend pas également, c'est que peut importe le fichier en plus, il me retourne seulement 5 lignes, c'est bien ce que me donne mon affichage en console:



    Il semble se stopper avant le contenu du fichier lui-même, comme s'il n'arrivait pas à le lire ou reconnaitre les caractères ?

    Si je test avec un fichier .txt, même avec plusieurs lignes il me retourne 0...
      0  0

  19. #19
    Responsable 2D/3D/Jeux

    Bonjour,

    Il faut ouvrir le fichier en mode binaire et non texte, sinon la lecture s'arrête au premier '\0' (fin de fichier).

    Aussi, je doute que le getline() fonctionne comme on s'y attend, en mode binaire.
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  20. ###raw>post.musername###
    Membre à l'essai
    Et du coup comment tu fais pour supprimer cette parti:

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
     
    ----------------------------599680733712361081007141
    Content-Disposition: form-data; name="file"; filename="2.png"
    Content-Type: image/png


    T'es obligé de l'ouvrir en mode text pour ça non ?

    Du coup j'ai changé par ça:

    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
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
     
     
    std::ofstream secondfile("secondFile.txt", std::ios::in | std::ios_base::binary); //Fichier crée dans ce répertoire
     
    	auto exPath = path.string() + "\\bufferFile.bin";
    	std::ifstream filetemp(exPath, std::ios::in | std::ios::binary);
    	std::cout << exPath << std::endl;
     
    if (filetemp.good())
    	{
     
    		long i;
    		for ( i = 0; getline(filetemp, line); ++i)
    		{
     
    			//std::cout << line << std::endl;
     
    			if (std::regex_search(line, expressionContent))
    			{
    				//std::cout << line << std::endl;
    				//secondfile << line;
     
    			}
    			else
    			{
    				std::cout << line << std::endl;
    			}
     
    			/*if (line != content_type)
    			{
    				std::cout << line << std::endl;
    				secondfile << line;
     
    			}*/
     
    		}
    		std::cout << "number of lines: " << i << std::endl;
    	}


    Mais dans les deux cas, soit j'ai rien, soit j'ai tout le contenu avec l'en tête content type...
    Je vois pas comment résoudre ce problème j'y ai passé la journée complète.
      0  0