Page 1 of 2

Enviar por POST a webservice

PostPosted: Thu Jan 06, 2022 12:21 pm
by cmsoft
Estimados:
Estoy intentando crear una clase para poder subir datos a un especie de webservice mediante POST
Digo "una especie" porque el "webservice" lo estoy haciendo yo.
Aclaro que no soy experto en ninguna de las dos cosas.
El tema es que funciona bien todo, EXCEPTO como graba la imagen en el servidor.
Los datos de campos y fichero de texto plano (el .prg) está siendo enviado bien y lo puedo ver perfectamente en el servidor
Pero la imagen no es leíble (también me paso con un archivo zip). Intuyo que debe ser la codificación pero no le encuentro la vuelta
Dejo el código por si algún experto me puede dar una mano.
Code: Select all  Expand view

#include "Fivewin.ch"
FUNCTION Main()
LOCAL oPost
oPost := TPostHtml():New()
oPost:AddFile("file", "miimagen.jpg", "c:\fwh\bitmaps\olga1.jpg")
oPost:AddFile("file1", "miprg.prg", "c:\fwh\samples\atest.prg")
oPost:AddField("campo1","valor1")
oPost:AddField("campo2","valor2")
oPost:SendReq("https://miweb/webservice")
MsgInfo(oPost:Status)
oPost:End()
RETURN nil

La clase es esta:
Code: Select all  Expand view

CLASS TPostHtml

   DATA   cDelimitador
   DATA   cDelimitador_base
   DATA   oStream
   DATA   Status

   METHOD New( ) CONSTRUCTOR

   METHOD AddField(cField, cValue)

   METHOD AddFile(cFieldName, cFileName, cFilePath)

   METHOD SendReq(cURL)

   METHOD End() INLINE ::oStream:Close
ENDCLASS

METHOD New( ) CLASS TPostHtml

        ::cDelimitador_base := Replicate( "-",6) +"1234567890"
        ::cDelimitador      := "--" + ::cDelimitador_base
        ::oStream  := CreateObject("ADODB.Stream")
        ::oStream:Mode := 3
        ::oStream:Charset := "Windows-1252"
        ::oStream:Open()
        ::Status  := nil

return Self

METHOD AddField(cField, cValue ) CLASS TPostHtml
::oStream:WriteText(::cDelimitador+CHR(10) + ;
            'Content-Disposition: form-data; name="'+ cField + '";'+ CHR(10) + CHR(10)+;
            cValue + CHR(10))
RETURN nil

METHOD AddFile(cFieldName, cFileName, cFilePath) CLASS TPostHtml
    LOCAL oByteArray, oStream
    oStream := CreateObject("ADODB.Stream")
        //Objeto ADODB stream usado para leer archivo binario
        WITH OBJECT oStream
            :Charset := "Windows-1252"
            :Type := 1
            :Mode := 3
            :Open()
            :LoadFromFile(cFilePath)
            //oByteArray := :Read()
        END                
        // Escribe datos binarios sobre stream de salida
        WITH OBJECT ::oStream
            :WriteText(::cDelimitador + CHR(10))
            :WriteText('Content-Disposition: form-data; name="'+ cFieldName + '"; filename="'+ cFileName + '"' +  CHR(10))
            :WriteText('Content-Type: "'+ GetContentType(cFileName) + '"' + CHR(10) + CHR(10))
            :Position := 0
            :Type := 1
            :Position := :Size()
            oStream:CopyTo(::oStream)
            :Position := 0
            :Type := 2
            :Position := :Size()
            :WriteText(CHR(10))
        End
RETURN nil            

METHOD SendReq(cURL) CLASS TPostHtml
LOCAL oXmlHttp, bytData

        //Add end boundary and read as byte array Agregar final de delimitador y leer array de bytes
        ::oStream:WriteText(::cDelimitador+ "--")
        ::oStream:Position := 0
        ::oStream:Type := 1
        bytData := ::oStream:Read()

        oXmlHttp := Createobject("MSXML2.ServerXMLHTTP")
        oXmlHttp:SetTimeouts(0, 60000, 300000, 300000)
        oXmlHttp:Open("POST",cURL,.f.)
        oXmlHttp:SetRequestHeader("Content-type", "multipart/form-data; boundary=" + ::cDelimitador_base)
        oXMLHTTP:setRequestHeader("Connection", "close")
        oXMLHTTP:setRequestHeader("Content-length", ::oStream:Size)
        oXmlHttp:Send( bytData)
        ::Status := oXMLHTTP:statusText

        oXmlHttp := nil

RETURN nil


STATIC function GetContentType(cFileName)
LOCAL cExt := SUBSTR(cFileName,At(".",cFileName)+1,LEN(cFilename) - At(".",cFileName)-1) ,;
      aTipeFile := {;
                { "php", "application/x-php"},;
                { "vbs", "application/x-vbs"},;
                { "jpe", "image/jpeg"},;
                { "jpg", "image/jpeg"},;
                { "jpeg", "image/jpeg"},;
                { "gif", "image/gif"},;
                { "png", "image/png"},;
                { "bmp", "image/bmp"},;
                { "ico", "image/x-icon"},;
                { "svg", "image/svg+xml"},;
                { "svgz", "image/svg+xml"},;
                { "tif", "image/tiff"},;
                { "tiff", "image/tiff"},;
                { "pct", "image/x-pict"},;
                { "psd", "image/vnd.adobe.photoshop"},;
                { "aac", "audio/x-aac"},;
                { "aif", "audio/x-aiff"},;
                { "flac", "audio/x-flac"},;
                { "m4a", "audio/x-m4a"},;
                { "m4b", "audio/x-m4b"},;
                { "mid", "audio/midi"},;
                { "midi", "audio/midi"},;
                { "mp3", "audio/mpeg"},;
                { "mpa", "audio/mpeg"},;
                { "mpc", "audio/x-musepack"},;
                { "oga", "audio/ogg"},;
                { "ogg", "audio/ogg"},;
                { "ra", "audio/vnd.rn-realaudio"},;
                { "ram", "audio/vnd.rn-realaudio"},;
                { "snd", "audio/x-snd"},;
                { "wav", "audio/x-wav"},;
                { "wma", "audio/x-ms-wma"},;
                { "avi", "video/x-msvideo"},;
                { "divx", "video/divx"},;
                { "flv", "video/x-flv"},;
                { "m4v", "video/mp4"},;
                { "mkv", "video/x-matroska"},;
                { "mov", "video/quicktime"},;
                { "mp4", "video/mp4"},;
                { "mpeg", "video/mpeg"},;
                { "mpg", "video/mpeg"},;
                { "ogm", "application/ogg"},;
                { "ogv", "video/ogg"},;
                { "rm", "application/vnd.rn-realmedia"},;
                { "rmvb", "application/vnd.rn-realmedia-vbr"},;
                { "smil", "application/x-smil"},;
                { "webm", "video/webm"},;
                { "wmv", "video/x-ms-wmv"},;
                { "xvid", "video/x-msvideo"},;
                { "js", "application/javascript"},;
                { "xml", "text/xml"},;
                { "html", "text/html"},;
                { "css", "text/css"},;
                { "txt", "text/plain"},;
                { "py", "text/x-python"},;
                { "pdf", "application/pdf"},;
                { "xhtml", "application/xhtml+xml"},;
                { "zip", "application/x-zip-compressed, application/zip"},;
                { "rar", "application/x-rar-compressed"},;
                { "cmd", "application/cmd"},;
                { "bat", "application/x-bat, application/x-msdos-program"},;
                { "exe", "application/exe, application/x-ms-dos-executable"},;
                { "msi", "application/x-msi"},;
                { "bin", "application/x-binary"},;
                { "crt", "application/x-x509-ca-cert"},;
                { "crl", "application/x-pkcs7-crl"},;
                { "pfx", "application/x-pkcs12"},;
                { "p12", "application/x-pkcs12"},;
                { "odc", "application/vnd.oasis.opendocument.chart"},;
                { "odf", "application/vnd.oasis.opendocument.formula"},;
                { "odb", "application/vnd.oasis.opendocument.database"},;
                { "odg", "application/vnd.oasis.opendocument.graphics"},;
                { "odi", "application/vnd.oasis.opendocument.image"},;
                { "odp", "application/vnd.oasis.opendocument.presentation"},;
                { "ods", "application/vnd.oasis.opendocument.spreadsheet"},;
                { "odt", "application/vnd.oasis.opendocument.tex"},;
                { "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},;
                { "dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},;
                { "potx", "application/vnd.openxmlformats-officedocument.presentationml.template"},;
                { "ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},;
                { "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},;
                { "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},;
                { "xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"},;
                { "ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"},;
                { "ppa", "application/vnd.ms-powerpoint"},;
                { "potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"},;
                { "ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"},;
                { "xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"},;
                { "pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"},;
                { "dotm", "application/vnd.ms-word.template.macroEnabled.12"},;
                { "docm", "application/vnd.ms-word.document.macroEnabled.12"},;
                { "doc", "application/msword"},;
                { "dot", "application/msword"},;
                { "pps", "application/mspowerpoint"},;
                { "ppt", "application/mspowerpoint,application/powerpoint,application/vnd.ms-powerpoint,application/x-mspowerpoint"},;
                { "xls", "application/vnd.ms-excel"},;
                { "xlt", "application/vnd.ms-excel"}}, i, cTipo :=  "text/plain"
FOR i := 1 TO LEN(aTipeFile)
    IF(aTipeFile[i,1] = cExt)
       cTipo := aTipeFile[i,2]                
    ENDIF  
NEXT i
RETURN cTipo
 

Agradezco cualquier sugerencia

Re: Enviar por POST a webservice

PostPosted: Thu Jan 06, 2022 1:53 pm
by leandro
Amigo como vas?

Nosotros hacemos de la siguiente manera, del lado del cliente convertimos el archivo a texto, apoyándonos en la funciones mime; luego en el servidor, mediante php, hacemos la conversión del mismo para que este quede legible.

Esto lo podemos hacer, cuando tenemos el control en los dos lados, tanto en el cliente como en el servidor, para poder hacer la conversión de texto a archivo y viceversa, pero desconozco como se haría cuando necesitemos subir el archivo en su formato original :shock:

Espero sea de utilidad

Del lado del cliente...
Code: Select all  Expand view

//CONVERTIMOS EL PDF A CADENA DE TEXTO
fMimeEnc( ::cNombrePDF, rutaTXT )
cText := MemoRead( rutaTXT )
::hDocumento["nomina"]["encabezado"]["contenidopdf"] := STRTRAN(cText,CRLF,'')
::cdnaJson := hb_jsonEncode( ::hDocumento ) //Lo guardamos en un json
 


Del lado del servidor
Code: Select all  Expand view


//recogemos los datos que vienen en el json
$datos = json_decode(file_get_contents("php://input"),true);
.....

$cntpdf = $datos["nomina"]["encabezado"]["contenidopdf"];
....
$nmarchivo = 'pdf_'.$consec.'_'.$ambient.'.pdf';
$rtarchivo = $rta_pdfs.$nmarchivo;
file_put_contents($rtarchivo, base64_decode($cntpdf)); //Esta función se encarga de restaurar el archivo

 

Re: Enviar por POST a webservice

PostPosted: Thu Jan 06, 2022 4:50 pm
by Otto

Re: Enviar por POST a webservice

PostPosted: Thu Jan 06, 2022 5:02 pm
by cmsoft
Gracias por las respuestas!
Leandro:
Si tengo el control de ambos lados, aunque del lado del servidor uso Laravel, y no estoy pudiendo reconvertir el archivo, pero sigo intentando.
Igualmente quería hacer algo mas genérico para llenar un formulario por post que pudiera servir para cualquier situación.
Otto:
He visto el código pero no veo la forma de convertir el archivo subido.
Muchas gracias a ambos.

Re: Enviar por POST a webservice

PostPosted: Thu Jan 06, 2022 5:44 pm
by Antonio Linares

Re: Enviar por POST a webservice

PostPosted: Thu Jan 06, 2022 7:40 pm
by Otto
Cesar,

As I said, I built my sample without using the complete TWeb. I extracted all the functions and built a standalone upload.prg.

I think you need GetMsgUpload() from twebapache.prg.

Charly: I don't know it we are allowed to repost TWeb source code?

Best regards,
Otto

Re: Enviar por POST a webservice

PostPosted: Thu Jan 06, 2022 8:00 pm
by Carles
Otto wrote:Cesar,

As I said, I built my sample without using the complete TWeb. I extracted all the functions and built a standalone upload.prg.

I think you need GetMsgUpload() from twebapache.prg.

Charly: I don't know it we are allowed to repost TWeb source code?

Best regards,
Otto


Please, feel free for help others. Any code of mine has to serve to help.

Thanks.
C.

Re: Enviar por POST a webservice

PostPosted: Thu Jan 06, 2022 8:17 pm
by Otto
Hello Cesar,
thanks to the generosity of Charly I may upload the source.
Charly, thank you so much.
Best regards,
Otto

You have to create a folder with a subfolder "upload"
Copy both files to the new folder and if you have installed mod harbour all should work fine.

TWebupload.prg
Code: Select all  Expand view

REQUEST DBFCDX
REQUEST DBFFPT

function main

TEMPLATE

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Button Upload</title>
    <meta charset="ISO-8859-1">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
    <link rel="shortcut icon" type="image/png" href="lib/tweb/images/tweb.png"/>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.css" rel="stylesheet"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.2/animate.min.css" rel="stylesheet">
    <script src="lib/tweb/lightbox/lightbox.js"></script>
    <link href="lib/tweb/lightbox/css/lightbox.css" rel="stylesheet" >
    <script src="lib/tweb/notify/bootstrap-notify.js"></script>
    <script src="lib/tweb/bootbox/bootbox.all.min.js"></script>
    <link href="lib/tweb/tweb.css" rel="stylesheet">
    <style>
        .thumbnailUpload{
            height: 100px;
            margin: 10px;
            display: inline-block;
        }
       
       
    </style>
    <div class="alert alert-dark form_title" role="alert">
        <h5 style="margin:0px;">
            <i class="far fa-share-square"></i> Test Button Upload
        </h5>
    </div>
    <div class="container" ><div class="col-6" >
        <input type="file" id="myupload" style="display:none;" accept="image/*" capture/>
       
       
        <button type="button" class="btn btn-primary  btn-primary" onclick="UploadFile()" id="_myupload" name="_myupload" value=""  >
            Upload</button>
        </div> 
       
        <img class="thumbnailUpload" id="blah" src="#" alt="your image" />
<!--  

        <div class="modal-footer">
            <button type="button" class="btn btn-secondary mr-auto" data-dismiss="modal">Close</button>
            <!--<button type="button" class="btn btn-primary">Save changes</button>-->
            <button type="button" id="processButton" class="btn btn-primary disabled" data-toggle="modal" href="#confirmModal">Upload
            </button>
        </div>
        --> 
        <script>
           
            function UploadFile() {            
               
                var o = new TWebUpload( 'myupload', 'srv_upload.prg', Post_UploadFile )
               
                o.Init()                   
            }
           
            function Post_UploadFile( dat ) {
               
                console.log( 'Post_UploadFile', dat )
               
                alert( 'File uploaded' )
            }
           
            function TWebUpload( cId, cUrl, callback, oData ) {
               
                this.cId        = ( typeof( cId ) === 'string') ? cId : '';
                this.cUrl       = ( typeof( cUrl ) === 'string') ? cUrl : '';
                this.callback   = ( typeof( callback ) === 'function') ? callback : null;
                this.onprogress 
                this.onloadstart
                this.onloadend
                this.onreading
                this.oData      = oData
               
                var Self    = this
                var reader  = null
               
                this.Init = function() {           
                   
                    //  Solo crearemos una vez el evento al id del DOM, sino dispararia una pila
                    //  del evento tantas veces como ejecutemos
                   
                    if (document.getElementById( this.cId ).getAttribute('listener') !== 'true') {         
                        document.getElementById( this.cId ).addEventListener('change',this.handleFileSelect, false);
                        document.getElementById( this.cId ).setAttribute('listener', 'true');   
                       
                    }
                   
                    $( '#' + this.cId ).trigger('click')       
                }
               
                this.errorHandle = function(evt) {
                    switch(evt.target.error.code) {
                        case evt.target.error.NOT_FOUND_ERR:
                        console.log('File Not Found!');
                        break;
                        case evt.target.error.NOT_READABLE_ERR:
                        console.log('File is not readable');
                        break;
                        case evt.target.error.ABORT_ERR:
                        break; // noop
                        default:
                        console.log('An error occurred reading this file.');
                    };
                }   
               
               
                this.handleFileSelect = function(evt) {    
                   
                    reader = new FileReader();
                   
                    reader.onerror      = this.errorHandle;
                   
                    reader.onprogress   = function(e) {
                       
                        if ( typeof Self.onreading === "function") {
                            var n100 = Math.floor(e.loaded / e.total * 100);
                            Self.onreading.apply(null, [e, n100] );                        
                        }
                    };
                   
                    reader.onabort      = function(e) {
                        //console.log('File read cancelled');
                    };
                   
                    reader.onloadend = function(e) {
                       
                        if ( typeof Self.onloadend === "function") {
                            Self.onloadend.apply(null, [e] );                          
                        }
                    };
                   
                    reader.onloadstart = function(e) {         
                       
                        if ( typeof Self.onloadstart === "function") {
                            Self.onloadstart.apply(null, [e] );                        
                        }                                  
                    };
                   
                    reader.onload = function(e) {   
                       
                        var file    = evt.target.files[0]          
                       
                        console.log( 'oData', $.type( Self.oData ) )
                       
                        var formData = new FormData()
                        $('#blah').attr('src', e.target.result);
                        //alert("upload")
                       
                        /*
                        if ( $.type( Self.oData ) == 'object' ) {                      
                            $.each(Self.oData, function (key, val) {           
                                formData.append( key, val )              
                            })                     
                        }          
                        */

                       
                        var blob = new Blob( [e.target.result], {type: "application/octet-stream"} );          
                       
                        var y = new Object()                   
                        y[ 'name' ] = file.name
                        y[ 'size' ] = file.size
                        y[ 'type' ] = file.type
                       
                        var z = new Object()
                        z[ 'var1' ] = 1234
                        z[ 'var2' ] = 'Maria de la O'
                       
                        formData.append( 'blob', blob );
                        console.log( 'blob data', blob);   
                       
                        formData.append( 'file', JSON.stringify(y) );   
                       
                        if ( $.type( oData ) == 'object' )         
                        formData.append( 'data', JSON.stringify(oData) );   
                       
                        $.ajax({
                            url: Self.cUrl,
                            data: formData,
                            processData: false,
                            // This will override the content type header,
                            // regardless of whether content is actually sent.
                            // Defaults to 'application/x-www-form-urlencoded'
                            contentType: 'multipart/form-data',
                            beforeSend: function(xhr) {
                                xhr.setRequestHeader('Content-Type', 'multipart/form-data');
                            },         
                            type: 'POST',
                            success: function ( dat ) {                
                               
                                if ( typeof Self.callback === "function") { 
                                   
                                    Self.callback.apply(null, [dat] );
                                   
                                } else {
                                   
                                    if ( typeof dat == 'object' )
                                    if ( dat.success )
                                    MsgInfo(dat.html, 'My Message')
                                    else
                                    MsgError(dat.error)
                                    else
                                    MsgError(dat)                      
                                }                                  
                               
                            },
                            error: function(data, textStatus, jqXHR) {
                               
                                console.log( 'Error data', data);          
                                console.log( 'Error textStatus', textStatus);
                                console.log( 'Error XHR', jqXHR);
                            },             
                            xhr: function() {
                                var xhr = $.ajaxSettings.xhr();
                               
                                xhr.upload.onprogress = function(e) {
                                   
                                    //  Sending file...
                                   
                                    var n100 = Math.floor(e.loaded / e.total *100);
                                   
                                    if ( typeof Self.onprogress === "function") {
                                        Self.onprogress.apply(null, [e, n100] );                           
                                    }                      
                                }
                               
                                return xhr;
                            }                              
                           
                        });   
                    }
                   
                    // Read in the image file as a binary string.
                    reader.readAsDataURL( evt.target.files[0] )    
                   
                    /*
                    for (i = 0; i < evt.target.files.length; i++) {
                        reader.readAsDataURL( evt.target.files[i] )    
                    }
                    */

                   
                }
            }
           
           
           
           
           
           
           
        </script>      
    </div>
   
   
    ENDTEXT
   
    return
   
    INIT PROCEDURE PrgInit
   
    SET CENTURY ON
    SET EPOCH TO YEAR(DATE())-98
   
    SET DELETED ON
    SET EXCLUSIVE OFF
   
    REQUEST HB_Lang_DE
   
    HB_LangSelect("DE")
   
    SET DATE TO GERMAN
   
    rddsetdefault( "DBFCDX" )
   
    EXTERN DESCEND
   
    RETURN
   
    //----------------------------------------------------------------------------//
 



srv_upload.prg
Code: Select all  Expand view



/*  ---------------------------------------------------------------------------
GetMsgUpload() devuelve la informacion de subida de un fichero en un hash:
blob - Fichero decodificado
file - hash con info de fichero: name, type, size, ext
data - hash con variables adicionales que se han enviado junto al fichero
--------------------------------------------------------------------------- */


#include "hbclass.ch"

function main()

local cPath     := hb_getenv( 'PRGPATH' ) + '/upload/'
local h         := GetMsgUpload()   
local lSuccess  := .f.
local cFile    
AP_SetContentType( "application/json" )    

cFile       := cPath + h[ 'file' ][ 'name' ]           



lSuccess    := hb_MemoWrit( cFile , h[ 'blob' ] )   

?? hb_jsonencode( { 'success' => lSuccess, 'info' => h[ 'file' ], 'data' => h[ 'data' ] } ) 

retu nil



/*  ---------------------------------------------------------------------------
GetMsgUpload() devuelve la informacion de subida de un fichero en un hash:
blob - Fichero decodificado
file - (opcional) hash con info de fichero: name, type, size, ext
data - (opcional) hash con variables adicionales que se han enviado junto al fichero
--------------------------------------------------------------------------- */


function GetMsgUpload()

local hParam    := ZAP_BodyPairs() 
local h         := {=>}

h[ 'blob' ]     := Hb_Base64Decode( hParam[ 'blob' ] )

if HB_HHasKey( hParam, 'file' )
h[ 'file' ]             := hb_jsonDecode( hParam[ 'file' ] )    
h[ 'file' ][ 'ext' ]    := lower( cFileExt( h[ 'file' ][ 'name' ]  ) )
else
h[ 'file' ]             := nil
endif

if HB_HHasKey( hParam, 'data' )
h[ 'data' ]     := hb_jsonDecode( hParam[ 'data' ] )
else
h[ 'data' ]     := nil
endif


retu h



#define TAG_DISPOSITION     'Content-Disposition: form-data;'
#define TAG_END             '------'

function ZAP_BodyPairs()
local oError
local bLastHandler := Errorblock( {|oError| MyError( oError ), Break(oError) } )
local o            

o := BodyPairs():New() 

ErrorBlock(bLastHandler) // Restore handler  

retu o:hData

function MyError( oError )
//? 'Error'
//? '====='
//? oError
retu nil


CLASS BodyPairs

DATA hData      INIT {=>}

METHOD New()

METHOD Extract()
METHOD ProcData()

ENDCLASS

//----------------------------------------------------------------------------//

METHOD New( cData ) CLASS BodyPairs

local cPart := ''

DEFAULT cData TO AP_Body()      //AP_PostPairs()   
//DEFAULT cData := GetData()

while ::Extract( @cData, @cPart )

::ProcData( cPart )

end

RETU Self



METHOD Extract( cData, cPart ) CLASS BodyPairs

local nIni      := hb_At( TAG_DISPOSITION , cData )
local nEnd     
local lFound    
local cStrIni, cStrFi

DEFAULT cData TO ''
DEFAULT cPart TO ''

if nIni == 0
retu .f.
endif

nEnd    := hb_At( TAG_END, cData, nIni )
lFound  := ( nIni > 0 .and. nEnd > 0 )

if lFound

cPart   := alltrim(Substr( cData, ( nIni + len( TAG_DISPOSITION ) ), ( nEnd - nIni - len( TAG_DISPOSITION ) - 2 ) ))

cStrIni := Substr( cData, 1, nIni-1 )
cStrFi  := Substr( cData, nEnd )
cData   := cStrIni + cStrFi

endif

retu lFound

METHOD ProcData( cPart ) CLASS BodyPairs

local nStart, nEnd, cVarName

DEFAULT cPart TO ''

//  check name var

if ( nStart := hb_At( 'name="', cPart ) ) == 0
retu nil
endif

if ( nEnd   := hb_At( '"', cPart, nStart + 7 ) ) == 0
retu nil
endif


cVarName := Alltrim(Substr( cPart, nStart + 6 , nEnd - (nStart+6) ))


//  check si file upload

if ( nStart := hb_At( "base64,", cPart ) ) > 0         
::hData[ cVarName ] := Substr( cPart, nStart + 7 )                     
else       
::hData[ cVarName ] := Alltrim( Substr( cPart, nEnd + 1 ))         
endif

retu nil



//----------------------------------------------------------------------------//
/*  Example de quan enviem formdata amb molt de contingut....

"------WebKitFormBoundaryf5o7two9i5wUlZtv\r\n
Content-Disposition: form-data; name": "clip.png"; filename="blob"
Content-Type: application/octet-stream

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAAGa2tLEAAAAAXNSR0IArs4c6QAAADBQTFRFgAAAAAAAAIAAgIAAAACAgACAAICAgICAwMDA/wAAAP8A//8AAAD//wD/AP//////tw+rZwAAAAF0Uk5TAEDm2GYAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfeCw4KOyB5pNv7AAAAL0lEQVQI12NgQAICDPgAuziMxcgIJAQFMQkBkAQj1BhBAZhqRhhNkMEuwCCOZCUAYNoBPf68fOsAAAAASUVORK5CYII=
------WebKitFormBoundaryf5o7two9i5wUlZtv
Content-Disposition: form-data; name="database.png"; filename="blob"
Content-Type: application/octet-stream

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAAGa2tLEAAAAAXNSR0IArs4c6QAAADBQTFRFgAAAAAAAAIAAgIAAAACAgACAAICAgICAwMDA/wAAAP8A//8AAAD//wD/AP//////tw+rZwAAAAF0Uk5TAEDm2GYAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfeCw4KOyB5pNv7AAAAL0lEQVQI12NgQAICDPgAuziMxcgIJAQFMQkBkAQj1BhBAZhqRhhNkMEuwCCOZCUAYNoBPf68fOsAAAAASUVORK5CYII=
------WebKitFormBoundaryf5o7two9i5wUlZtv
Content-Disposition: form-data; name="test11"

hola guapu
------WebKitFormBoundaryf5o7two9i5wUlZtv
Content-Disposition: form-data; name="test22"

Adios Baby
------WebKitFormBoundaryf5o7two9i5wUlZtv--
"
*/




function cFileExt( cPathMask ) // returns the ext of a filename

local cExt := AllTrim( cFileNoPath( cPathMask ) )
local n    := RAt( ".", cExt )

return AllTrim( If( n > 0 .and. Len( cExt ) > n,;
Right( cExt, Len( cExt ) - n ), "" ) )

//----------------------------------------------------------------------------//



function cFileNoPath( cPathMask )  // returns just the filename no path

local n := RAt( "/", cPathMask )

return If( n > 0 .and. n < Len( cPathMask ),;
Right( cPathMask, Len( cPathMask ) - n ),;
If( ( n := At( ":", cPathMask ) ) > 0,;
Right( cPathMask, Len( cPathMask ) - n ),;
cPathMask ) )          




 

Re: Enviar por POST a webservice

PostPosted: Fri Jan 07, 2022 11:38 am
by cmsoft
Gracias a todos por las respuestas.
He analizado los códigos que me pasaron, pero no he sido capaz de hacer que pueda enviar el formulario con el archivo de imagen comprensible del lado del servidor.
Sigo investigando...

Re: Enviar por POST a webservice

PostPosted: Fri Jan 07, 2022 12:17 pm
by cmsoft
Después de unas pruebas y gracias a la ayuda del foro, he logrado enviar y recibir los datos correctamente
Seguí los pasos que me sugirió Leandro, ya que tenía control de ambos lados del asunto.
La clase quedó así al final
Code: Select all  Expand view

CLASS TPostHtml

   DATA   cDelimitador
   DATA   cDelimitador_base
   DATA   oStream
   DATA   Status
   DATA   aExtraRequestHeader INIT {}
   DATA   cCharSet INIT "Windows-1252"

   METHOD New( ) CONSTRUCTOR

   METHOD AddField(cField, cValue)

   METHOD AddFile(cFieldName, cFileName, cFilePath)

   METHOD AddExtraRequest(cClave,cValor)

   METHOD SendReq(cURL)

   METHOD End() INLINE ::oStream:Close
ENDCLASS

METHOD New( cCharSet ) CLASS TPostHtml
DEFAULT cCharSet := ::cCharSet
        ::cDelimitador_base := Replicate( "-",6) +"1234567890"
        ::cDelimitador      := "--" + ::cDelimitador_base
        ::oStream  := CreateObject("ADODB.Stream")
        ::oStream:Mode := 3
        ::oStream:Charset := cCharSet
        ::oStream:Open()
        ::Status  := nil

return Self

METHOD AddField(cField, cValue ) CLASS TPostHtml
::oStream:WriteText(::cDelimitador+CHR(10) + ;
            'Content-Disposition: form-data; name="'+ cField + '";'+ CHR(10) + CHR(10)+;
            cValue + CHR(10))
RETURN nil

METHOD AddFile(cFieldName, cFileName, cFilePath) CLASS TPostHtml
    LOCAL oByteArray, oStream, cText
fMimeEnc( cFilePath, '.\prueba.txt' )   
cText := MemoRead( '.\prueba.txt' )
cText := STRTRAN(cText,CRLF,'')
cText := hb_jsonEncode(cText)
memowrit( ".\prueba", cText )
    oStream := CreateObject("ADODB.Stream")
        //Objeto ADODB stream usado para leer archivo binario
        WITH OBJECT oStream
            :Charset := ::cCharSet
            :Type := 1
            :Mode := 3
            :Open()
            :LoadFromFile('.\prueba.txt')
            //oByteArray := :Read()
        END                
        // Escribe datos binarios sobre stream de salida
        WITH OBJECT ::oStream
            :WriteText(::cDelimitador + CHR(10))
            :WriteText('Content-Disposition: form-data; name="'+ cFieldName + '"; filename="'+ cFileName + '"' +  CHR(10))
            :WriteText('Content-Type: "'+ GetContentType(cFileName) + '"' + CHR(10) + CHR(10))
            :Position := 0
            :Type := 1
            :Position := :Size()
            oStream:CopyTo(::oStream)
            :Position := 0
            :Type := 2
            :Position := :Size()
            :WriteText(CHR(10))
        End
RETURN nil  


METHOD AddExtraRequest(cClave,cValor) CLASS TPostHtml

AADD(::aExtraRequestHeader,{cClave,cValor})

RETURN nil        

METHOD SendReq(cURL) CLASS TPostHtml
LOCAL oXmlHttp, bytData, i

        //Add end boundary and read as byte array Agregar final de delimitador y leer array de bytes
        ::oStream:WriteText(::cDelimitador+ "--")
        ::oStream:Position := 0
        ::oStream:Type := 1
        bytData := ::oStream:Read()

        oXmlHttp := Createobject("MSXML2.ServerXMLHTTP")
        oXmlHttp:SetTimeouts(0, 60000, 300000, 300000)
        oXmlHttp:Open("POST",cURL,.f.)
        oXmlHttp:SetRequestHeader("Content-type", "multipart/form-data; boundary=" + ::cDelimitador_base)
        FOR i := 1 TO LEN(::aExtraRequestHeader)
            oXmlHttp:SetRequestHeader(::aExtraRequestHeader[i,1],::aExtraRequestHeader[i,2])
        NEXT i
        oXMLHTTP:setRequestHeader("Connection", "close")
        oXMLHTTP:setRequestHeader("Content-length", ::oStream:Size)
        oXmlHttp:Send( bytData)
        ::Status := oXMLHTTP:statusText
        memowrit( "post.txt", bytData )
        oXmlHttp := nil

RETURN nil


STATIC function GetContentType(cFileName)
LOCAL cExt := SUBSTR(cFileName,At(".",cFileName)+1,LEN(cFilename) - At(".",cFileName)-1) ,;
      aTipeFile := {;
                { "php", "application/x-php"},;
                { "vbs", "application/x-vbs"},;
                { "jpe", "image/jpeg"},;
                { "jpg", "image/jpeg"},;
                { "jpeg", "image/jpeg"},;
                { "gif", "image/gif"},;
                { "png", "image/png"},;
                { "bmp", "image/bmp"},;
                { "ico", "image/x-icon"},;
                { "svg", "image/svg+xml"},;
                { "svgz", "image/svg+xml"},;
                { "tif", "image/tiff"},;
                { "tiff", "image/tiff"},;
                { "pct", "image/x-pict"},;
                { "psd", "image/vnd.adobe.photoshop"},;
                { "aac", "audio/x-aac"},;
                { "aif", "audio/x-aiff"},;
                { "flac", "audio/x-flac"},;
                { "m4a", "audio/x-m4a"},;
                { "m4b", "audio/x-m4b"},;
                { "mid", "audio/midi"},;
                { "midi", "audio/midi"},;
                { "mp3", "audio/mpeg"},;
                { "mpa", "audio/mpeg"},;
                { "mpc", "audio/x-musepack"},;
                { "oga", "audio/ogg"},;
                { "ogg", "audio/ogg"},;
                { "ra", "audio/vnd.rn-realaudio"},;
                { "ram", "audio/vnd.rn-realaudio"},;
                { "snd", "audio/x-snd"},;
                { "wav", "audio/x-wav"},;
                { "wma", "audio/x-ms-wma"},;
                { "avi", "video/x-msvideo"},;
                { "divx", "video/divx"},;
                { "flv", "video/x-flv"},;
                { "m4v", "video/mp4"},;
                { "mkv", "video/x-matroska"},;
                { "mov", "video/quicktime"},;
                { "mp4", "video/mp4"},;
                { "mpeg", "video/mpeg"},;
                { "mpg", "video/mpeg"},;
                { "ogm", "application/ogg"},;
                { "ogv", "video/ogg"},;
                { "rm", "application/vnd.rn-realmedia"},;
                { "rmvb", "application/vnd.rn-realmedia-vbr"},;
                { "smil", "application/x-smil"},;
                { "webm", "video/webm"},;
                { "wmv", "video/x-ms-wmv"},;
                { "xvid", "video/x-msvideo"},;
                { "js", "application/javascript"},;
                { "xml", "text/xml"},;
                { "html", "text/html"},;
                { "css", "text/css"},;
                { "txt", "text/plain"},;
                { "py", "text/x-python"},;
                { "pdf", "application/pdf"},;
                { "xhtml", "application/xhtml+xml"},;
                { "zip", "application/x-zip-compressed, application/zip"},;
                { "rar", "application/x-rar-compressed"},;
                { "cmd", "application/cmd"},;
                { "bat", "application/x-bat, application/x-msdos-program"},;
                { "exe", "application/exe, application/x-ms-dos-executable"},;
                { "msi", "application/x-msi"},;
                { "bin", "application/x-binary"},;
                { "crt", "application/x-x509-ca-cert"},;
                { "crl", "application/x-pkcs7-crl"},;
                { "pfx", "application/x-pkcs12"},;
                { "p12", "application/x-pkcs12"},;
                { "odc", "application/vnd.oasis.opendocument.chart"},;
                { "odf", "application/vnd.oasis.opendocument.formula"},;
                { "odb", "application/vnd.oasis.opendocument.database"},;
                { "odg", "application/vnd.oasis.opendocument.graphics"},;
                { "odi", "application/vnd.oasis.opendocument.image"},;
                { "odp", "application/vnd.oasis.opendocument.presentation"},;
                { "ods", "application/vnd.oasis.opendocument.spreadsheet"},;
                { "odt", "application/vnd.oasis.opendocument.tex"},;
                { "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},;
                { "dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},;
                { "potx", "application/vnd.openxmlformats-officedocument.presentationml.template"},;
                { "ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},;
                { "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},;
                { "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},;
                { "xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"},;
                { "ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"},;
                { "ppa", "application/vnd.ms-powerpoint"},;
                { "potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"},;
                { "ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"},;
                { "xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"},;
                { "pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"},;
                { "dotm", "application/vnd.ms-word.template.macroEnabled.12"},;
                { "docm", "application/vnd.ms-word.document.macroEnabled.12"},;
                { "doc", "application/msword"},;
                { "dot", "application/msword"},;
                { "pps", "application/mspowerpoint"},;
                { "ppt", "application/mspowerpoint,application/powerpoint,application/vnd.ms-powerpoint,application/x-mspowerpoint"},;
                { "xls", "application/vnd.ms-excel"},;
                { "xlt", "application/vnd.ms-excel"}}, i, cTipo :=  "text/plain"
FOR i := 1 TO LEN(aTipeFile)
    IF(aTipeFile[i,1] = cExt)
       cTipo := aTipeFile[i,2]                
    ENDIF  
NEXT i
RETURN cTipo
 

Muchas gracias a todos por la ayuda

Re: Enviar por POST a webservice

PostPosted: Fri Jan 07, 2022 6:49 pm
by Antonio Linares
César,

gracias por compartirla!

Re: Enviar por POST a webservice

PostPosted: Fri Jul 22, 2022 10:34 am
by Ruth
Antonio Linares wrote:César,

Estos son los ejemplos incluidos con mod_harbour para enviar un fichero y luego salvarlo:

https://github.com/FiveTechSoft/mod_harbour/blob/master/samples/sendfile.prg

https://github.com/FiveTechSoft/mod_harbour/blob/master/samples/upload.prg


Dear Antonio,

please can you help me with the sendfile.prg & upload.prg example above.
When I open my uploaded img file it seems to be corrupt and it seems to have another encoding as the original jpg offline.
https://onedrive.live.com/?cid=A2B81CE57637B150&id=A2B81CE57637B150%2163656&parId=root&parQt=sharedby&o=OneUp
Image

Many thanks in advance and kind regards
Ruth

Re: Enviar por POST a webservice

PostPosted: Fri Jul 22, 2022 2:17 pm
by Antonio Linares
Dear Ruth,

The url that you have provided is not working. Could you send it to me by email using https://wormhole.app/ ? many thanks

Also, please send me the image that you are testing,

best regards

Re: Enviar por POST a webservice

PostPosted: Fri Jul 22, 2022 4:34 pm
by joseluisysturiz
Saludos, tengo una propuesta de un cliente para desarrollar un WEBSERVICE, queria saber sus recomendaciones con FW, si es que es posible usar este lenguaje en dicho desarrollo, algun POST o la informacion para iniciar , gracias... :shock:

Re: Enviar por POST a webservice

PostPosted: Fri Jul 22, 2022 4:54 pm
by Ruth
Dear Antonio,

many thanks for your kind attention. I sent the file to you with email.
I am looking forward very much to your answer ... your sendfile-sample is so nice and slim and I already learned so much from it.
Thank you again :-)

and kind regards
Ruth