FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour Enviar por POST a webservice
Posts: 1344
Joined: Wed Nov 16, 2005 09:14 PM
Enviar por POST a webservice
Posted: Thu Jan 06, 2022 12:21 PM

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.

#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 (fw): Select all Collapse
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

Posts: 1818
Joined: Wed Oct 26, 2005 02:49 PM
Re: Enviar por POST a webservice
Posted: Thu Jan 06, 2022 01:53 PM

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...

//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

//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
Saludos
LEANDRO AREVALO
Bogotá (Colombia)
https://hymlyma.com
https://hymplus.com/
leandroalfonso111@gmail.com
leandroalfonso111@hotmail.com

[ Turbo Incremental Link64 6.98 Embarcadero 7.70 ] [ FiveWin 25.01 ] [ xHarbour 64 bits) ]
Posts: 6984
Joined: Fri Oct 07, 2005 07:07 PM
Re: Enviar por POST a webservice
Posted: Thu Jan 06, 2022 04:50 PM
Posts: 1344
Joined: Wed Nov 16, 2005 09:14 PM
Re: Enviar por POST a webservice
Posted: Thu Jan 06, 2022 05:02 PM

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.

Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM
Re: Enviar por POST a webservice
Posted: Thu Jan 06, 2022 05:44 PM
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 6984
Joined: Fri Oct 07, 2005 07:07 PM
Re: Enviar por POST a webservice
Posted: Thu Jan 06, 2022 07:40 PM

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

Posts: 1283
Joined: Fri Feb 10, 2006 02:34 PM
Re: Enviar por POST a webservice
Posted: Thu Jan 06, 2022 08:00 PM
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.
Salutacions, saludos, regards

"...programar es fácil, hacer programas es difícil..."

UT Page -> https://carles9000.github.io/
Forum UT -> https://discord.gg/bq8a9yGMWh
HIX -> https://github.com/carles9000/hix
Posts: 6984
Joined: Fri Oct 07, 2005 07:07 PM
Re: Enviar por POST a webservice
Posted: Thu Jan 06, 2022 08:17 PM

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

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

/*  ---------------------------------------------------------------------------
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 ) )
Posts: 1344
Joined: Wed Nov 16, 2005 09:14 PM
Re: Enviar por POST a webservice
Posted: Fri Jan 07, 2022 11:38 AM

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...

Posts: 1344
Joined: Wed Nov 16, 2005 09:14 PM
Re: Enviar por POST a webservice
Posted: Fri Jan 07, 2022 12:17 PM

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

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

Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM
Re: Enviar por POST a webservice
Posted: Fri Jan 07, 2022 06:49 PM

César,

gracias por compartirla!

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 179
Joined: Fri Dec 07, 2007 01:26 PM
Re: Enviar por POST a webservice
Posted: Fri Jul 22, 2022 10:34 AM
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


Many thanks in advance and kind regards
Ruth
Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM
Re: Enviar por POST a webservice
Posted: Fri Jul 22, 2022 02:17 PM
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
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 2064
Joined: Fri Jan 06, 2006 09:28 PM
Re: Enviar por POST a webservice
Posted: Fri Jul 22, 2022 04:34 PM

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:

Dios no está muerto...



Gracias a mi Dios ante todo!
Posts: 179
Joined: Fri Dec 07, 2007 01:26 PM
Re: Enviar por POST a webservice
Posted: Fri Jul 22, 2022 04:54 PM

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