Post a file with 4d-plugin-curl-v2?

Hi,

I was using Keisuke’s cURL plugin to POST a web form with some text inputs and a blob (input type=‘file’).

I migrated on 4d-plugin-curl-v2 and I can’t do it anymore.
I see there’s an HTTPPOST option that’s not supported, but I guess it’s possible to do without.

Here’s what I did :

	$voPath:=File("/RESOURCES/zDIA/aboutLogo.png")
	$vxChangelog:=$voPath.getContent()
	
	$vtChangeLog:=BLOB to text($vxChangelog;UTF8 text without length)
	$vtRequest:="v=1&t=2&file="+$vtChangeLog
	TEXT TO BLOB($vtRequest;$vxRequest)
	
	
	$voCurlOptions:=New object
	$voCurlOptions.POST:=1
	$voCurlOptions.SSL_VERIFYPEER:=0
	$voCurlOptions.SSL_VERIFYHOST:=0
	$voCurlOptions.TIMEOUT:=20
	$voCurlOptions.DEBUG:=Get 4D folder(Logs folder)  // #norel pour #test
	$voCurlOptions.URL:="https://127.0.0.1/4DACTION/upload"
	
	$vlError:=cURL (JSON Stringify($voCurlOptions);$vxRequest;$vxResponse;"";$vtTransfertInfo;$vtHeaderInfo)
	
	$vlHttpStatus:=zWEBhttpStatusCodeGet (1;$vtHeaderInfo)
	
	If ($vlHttpStatus=200)
		$vtReponse:=BLOB to text($vxResponse;UTF8 text without length)
	End if 

If $voPath designates a text file, it’s ok.
But if it designates a BLOB, like a picture, I’m lost.

Any help ?
Thanks

Why use a CURL plugin for HTTP post when that is built-in 4D feature? See the “HTTP Request” command.

I agree with the previous post that native command should suffice for this kind of task (I originally shared the plugin for v11/v12).

That said, there seems to be a fundamental problem in how you are posting data over HTTP.

Your request in $vtRequest suggests that you want to post x-www-form-urlencoded to the server, although you also want to post data (image) that is not form url encoded. I would imagine that a multipart form data post (each part has its own content-type) is more appropriate.

It also seems problematic that you are trying to convert the image to text, using the UTF-8 encoding. UTF-8 imposes certain restrictions on byte sequence, so your image may get corrupt.

Ii is also problematic that your are using TEXT TO BLOB omitting the encoding, which implies MacRoman of up to 32K bytes.

Unless all these HTTP violations are resolved, I am not sure if switching to HTTP Request can actually solve your problem.

Indeed, I coded the original method with v12 where there was no “HTTP Request” command.
I forgot I could use it instead of curl v2. :roll_eyes:

What I want to POST is a multipart/form-data.
I know there’s a problem with TEXT TO BLOB and that it’s not the good way to go, but I don’t how to build my request payload properly since I’d like to send parameters like “v=2&file=myBlob”.
I’ll keep looking.

Thanks a lot for your feedbacks.

I managed to build the Request Payload, but I thought it would be simpler.
Here’s my draft :

	$vtBoundary:="---------------------------195171173737323592923174930273"
	
	SET BLOB SIZE($vxPayload;0)
	
	  // HTTP body
	  // "v=2"
	$vtTest:=""
	$vtTest:=$vtTest+$vtBoundary+"\r\n"
	$vtTest:=$vtTest+"Content-Disposition: form-data; name=\"v\"\r\n\r\n"
	$vtTest:=$vtTest+"2"+"\r\n"
	TEXT TO BLOB($vtTest;$vxPart;UTF8 text without length)
	COPY BLOB($vxPart;$vxPayload;0;BLOB size($vxPayload);BLOB size($vxPart))
	
	  // "t=3"
	$vtTest:=""
	$vtTest:=$vtTest+$vtBoundary+"\r\n"
	$vtTest:=$vtTest+"Content-Disposition: form-data; name=\"t\"\r\n\r\n"
	$vtTest:=$vtTest+"3"+"\r\n"
	TEXT TO BLOB($vtTest;$vxPart;UTF8 text without length)
	COPY BLOB($vxPart;$vxPayload;0;BLOB size($vxPayload);BLOB size($vxPart))
	
	  // "myFile=blob"
	$vtTest:=""
	$vtTest:=$vtTest+$vtBoundary+"\r\n"
	$vtTest:=$vtTest+"Content-Disposition: form-data; name=\"myFile\"; filename=\"toto.png\"\r\n"
	$vtTest:=$vtTest+"Content-Type: application/octet-stream\r\n\r\n"
	TEXT TO BLOB($vtTest;$vxPart;UTF8 text without length)
	COPY BLOB($vxPart;$vxPayload;0;BLOB size($vxPayload);BLOB size($vxPart))
	
	DOCUMENT TO BLOB("Macintosh HD:Users:julien:Desktop:aboutLogo.png";$vxFile)
	COPY BLOB($vxFile;$vxPayload;0;BLOB size($vxPayload);BLOB size($vxFile))
	
	$vtTest:=""
	$vtTest:=$vtTest+"3"+"\r\n"
	TEXT TO BLOB($vtTest;$vxPart;UTF8 text without length)
	COPY BLOB($vxPart;$vxPayload;0;BLOB size($vxPayload);BLOB size($vxPart))
	
	  // last boundary
	$vtTest:=""
	$vtTest:=$vtTest+$vtBoundary+"--\r\n"
	TEXT TO BLOB($vtTest;$vxPart;UTF8 text without length)
	COPY BLOB($vxPart;$vxPayload;0;BLOB size($vxPayload);BLOB size($vxPart))
	
	
	
	  // HTTP headers
	ARRAY TEXT($ttHeaderName;0x0000)
	ARRAY TEXT($ttHeaderVal;0x0000)
	
	APPEND TO ARRAY($ttHeaderName;"Content-Type")
	APPEND TO ARRAY($ttHeaderVal;"multipart/form-data; boundary="+Substring($vtBoundary;3))
	
	$vlHttpStatus:=HTTP Request(HTTP POST method;"http://127.0.0.1/4DACTION/LICactiver";$vxPayload;$vxResponse;$ttHeaderName;$ttHeaderVal)

Did I miss something to make it shorter ?

I think the code is a good as it can be.

Of course, one could use some refactoring to slim down repetitive patterns, but the basic premise seems solid.

Hi,

Here is the code I use for multi-part…

HTTP_multipartBoundaryNew :

//================================================================================
  //@xdoc-start : en
  //@name : HTTP_multipartBoundaryNew
  //@scope : public
  //@deprecated : no
  //@description : This function returns a unique boundary (using a uuid)
  //@parameter[0-OUT-boundary-TEXT] : unique boundary
  //@notes :
  //@example : HTTP_multipartBoundaryNew => "========================20872C9FA7474919A1750770F0FAD090"
  //@see : 
  //@version : 1.00.00
  //@author : Bruno LEGAY (BLE) - Copyrights A&C Consulting - 2008
  //@history : CREATION : Bruno LEGAY (BLE) - 10/11/2015, 10:36:21 - v1.00.00
  //@xdoc-end
  //================================================================================

C_TEXT($0;$vt_boundary)

$vt_boundary:=("="*24)+Generate UUID

$0:=$vt_boundary

BLB_append :

//================================================================================
  //@xdoc-start : en
  //@name : BLB_append
  //@scope : public
  //@deprecated : no
  //@description : this method is used to copy/append a blob ($2) at the end of another blob ($1) 
  //@parameter[1-IN-blobPtr-POINTER] : blob pointer (modified)
  //@parameter[2-IN-blobToAddPtr-POINTER] : blob to add pointer (not modified)
  //@notes :
  //@example : BLB_append 
  //@see : 
  //@version : 1.00.00
  //@author : Bruno LEGAY (BLE) - Copyrights A&C Consulting - 2008
  //@history : CREATION : Bruno LEGAY (BLE) - 19/01/2009, 20:11:24 - v1.00.00
  //@xdoc-end
  //================================================================================

C_POINTER($1;$vp_blobMainPtr)  //blob where we want to append data (at the end)
C_POINTER($2;$vp_blobAdditionPtr)  //blob to add at the end of the first blob

If (Count parameters>1)

$vp_blobMainPtr:=$1
$vp_blobAdditionPtr:=$2

If ((Type($vp_blobMainPtr->)=Is BLOB) & (Type($vp_blobAdditionPtr->)=Is BLOB))

C_LONGINT($vl_nbBytesToCopy)
$vl_nbBytesToCopy:=BLOB size($vp_blobAdditionPtr->)

If ($vl_nbBytesToCopy>0)

C_LONGINT($vl_srcOffset)
$vl_srcOffset:=0

C_LONGINT($vl_dstOffset)
$vl_dstOffset:=BLOB size($vp_blobMainPtr->)

  //adding all the bytes of blobAddition (starting at offset 0) at the end of blobMain
COPY BLOB($vp_blobAdditionPtr->;$vp_blobMainPtr->;$vl_srcOffset;$vl_dstOffset;$vl_nbBytesToCopy)

End if 

End if 

End if 

BLB_appendText :

//================================================================================
  //@xdoc-start : en
  //@name : BLB_appendText
  //@scope : public
  //@deprecated : no
  //@description : This method/function returns 
  //@parameter[0-OUT-paramName-TEXT] : ParamDescription
  //@parameter[1-IN-bufferBlobPtr-POINTER] : buffer blob pointer (modified)
  //@parameter[2-IN-encoding-TEXT] : encoding (optional, default value : "utf-8")
  //@notes :
  //@example : BLB_appendText 
  //@see : 
  //@version : 1.00.00
  //@author : Bruno LEGAY (BLE) - Copyrights A&C Consulting - 2008
  //@history : CREATION : Bruno LEGAY (BLE) - 11/09/2015, 16:55:13 - v1.00.00
  //@xdoc-end
  //================================================================================

C_POINTER($1;$vp_blobPtr)
C_TEXT($2;$vt_buffer)
C_TEXT($3;$vt_encoding)

C_LONGINT($vl_nbParam)
$vl_nbParam:=Count parameters
If ($vl_nbParam>1)
$vp_blobPtr:=$1
$vt_buffer:=$2

Case of 
: ($vl_nbParam=2)
$vt_encoding:="utf-8"

Else 
$vt_encoding:=$3
End case 

If (Type($vp_blobPtr->)=Is BLOB)

If (Length($vt_buffer)>0)

C_BLOB($vx_buffer)
SET BLOB SIZE($vx_buffer;0)
CONVERT FROM TEXT($vt_buffer;"utf-8";$vx_buffer)

C_LONGINT($vl_nbBytesToCopy)
$vl_nbBytesToCopy:=BLOB size($vx_buffer)
If ($vl_nbBytesToCopy>0)

C_LONGINT($vl_srcOffset)
$vl_srcOffset:=0

C_LONGINT($vl_dstOffset)
$vl_dstOffset:=BLOB size($vp_blobPtr->)

COPY BLOB($vx_buffer;$vp_blobPtr->;$vl_srcOffset;$vl_dstOffset;$vl_nbBytesToCopy)

End if 
SET BLOB SIZE($vx_buffer;0)

End if 

End if 

End if 

HTTP_multipartBuild :

//================================================================================
  //@xdoc-start : en
  //@name : HTTP_multipartBuild
  //@scope : public
  //@deprecated : no
  //@description : This method builds an multipart blob
  //@parameter[1-INOUT-$vp_bodyBlobPtr-POINTER]] : body blob pointer (modified)
  //@parameter[2-IN-boundary-TEXT] : boundary (see HTTP_multipartBoundaryNew)
  //@parameter[3-IN-header-TEXT] : part header (optional)
  //@parameter[4-IN-partBlobPtr-POINTER] : part body (optional, not modified)
  //@notes :
  //@example : 
  //C_TEXT($vt_boundary)
  //$vt_boundary:=HTTP_multipartBoundaryNew 
  //  //"========================0C832A9F8D41497F97DA591189E1DB80"
  //
  //C_TEXT($vt_crlf)
  //$vt_crlf:="\r\n"
  //
  //C_BLOB($vx_body)
  //SET BLOB SIZE($vx_body;0)
  //
  //C_TEXT($vt_header)
  //
  //  // set multipart 1
  //$vt_header:=""
  //$vt_header:=$vt_header+"Content-Disposition: form-data; name=\"frontImage\"; filename=\"frontImage.jpg\""+$vt_crlf
  //$vt_header:=$vt_header+"Content-Type: image/jpeg"+$vt_crlf
  //$vt_header:=$vt_header+"Content-Length: "+String(BLOB size($vx_frontImageBlob))+$vt_crlf
  //HTTP_multipartBuild (->$vx_body;$vt_boundary;$vt_header;->$vx_frontImageBlob)
  //
  //  // set multipart 2
  //$vt_header:=""
  //$vt_header:=$vt_header+"Content-Disposition: form-data; name=\"backImage\"; filename=\"backImage.jpg\""+$vt_crlf
  //$vt_header:=$vt_header+"Content-Type: image/jpeg"+$vt_crlf
  //$vt_header:=$vt_header+"Content-Length: "+String(BLOB size($vx_backimageblob))+$vt_crlf
  //HTTP_multipartBuild (->$vx_body;$vt_boundary;$vt_header;->$vx_backimageblob)
  //
  //  // end multipart
  //HTTP_multipartBuild (->$vx_body;$vt_boundary)
  //
  //ARRAY TEXT($tt_headerKeys;0)
  //ARRAY TEXT($tt_headerValues;0)
  //
  //APPEND TO ARRAY($tt_headerKeys;"Content-Type")
  //APPEND TO ARRAY($tt_headerValues;"multipart/form-data; boundary="+$vt_boundary)
  //
  //APPEND TO ARRAY($tt_headerKeys;"Content-Length")
  //APPEND TO ARRAY($tt_headerValues;String(BLOB size($vx_body)))
  //
  //@see : 
  //@version : 1.00.00
  //@author : Bruno LEGAY (BLE) - Copyrights A&C Consulting - 2008
  //@history : CREATION : Bruno LEGAY (BLE) - 10/11/2015, 10:30:56 - v1.00.00
  //@xdoc-end
  //================================================================================

C_POINTER($1;$vp_bodyBlobPtr)  // (modified)
C_TEXT($2;$vt_boundary)
C_TEXT($3;$vt_header)  // (optional)
C_POINTER($4;$vp_partBlobPtr)  // (optional, not modified)

C_LONGINT($vl_nbParam)
$vl_nbParam:=Count parameters

If ($vl_nbParam>1)
$vp_bodyBlobPtr:=$1
$vt_boundary:=$2

Case of 
: ($vl_nbParam=2)
$vt_header:=""

: ($vl_nbParam=3)
$vt_header:=$3

Else 
  //: ($vl_nbParam=4)
$vt_header:=$3
$vp_partBlobPtr:=$4
End case 

ASSERT((Type($vp_bodyBlobPtr->)=Is BLOB);"$1 should be a blob pointer")

C_TEXT($vt_crlf)
$vt_crlf:="\r\n"

C_TEXT($vt_boundaryDelim)
$vt_boundaryDelim:="--"

C_TEXT($vt_headerPart)
$vt_headerPart:=""
If (BLOB size($vp_bodyBlobPtr->)>0)  // start boundary with crlf except for first boundary
$vt_headerPart:=$vt_headerPart+$vt_crlf
End if 

If ($vl_nbParam>2)  // header provided
$vt_headerPart:=$vt_headerPart+$vt_boundaryDelim+$vt_boundary+$vt_crlf

If (Length($vt_header)>0)
ASSERT(Position($vt_crlf+$vt_crlf;$vt_header;*)=0;"Header should not contain 2 crlf")
ASSERT(Substring($vt_header;Length($vt_header)-1;2)="\r\n";"Header should end with crlf")
ASSERT(Substring($vt_header;1;2)#"\r\n";"Header should not start with crlf")

$vt_headerPart:=$vt_headerPart+$vt_header
End if 
$vt_headerPart:=$vt_headerPart+$vt_crlf
Else   // last boundary finishes with "--" (no crlf necessary)
$vt_headerPart:=$vt_headerPart+$vt_boundaryDelim+$vt_boundary+$vt_boundaryDelim  //+$vt_crlf
End if 

  // append multipart boundary (and possibly header)
BLB_appendText ($vp_bodyBlobPtr;$vt_headerPart;"us-ascii")

  // append multipart body 
If ($vl_nbParam>3)
If (Type($vp_partBlobPtr->)=Is BLOB)
BLB_append ($vp_bodyBlobPtr;$vp_partBlobPtr)
End if 
End if 

End if 

Great, thanks for your feedback.