Posting a File to a Web Service

Started by Jeffrey Ferreira, May 17, 2024, 09:47:02 PM

Previous topic - Next topic

Jeffrey Ferreira

Hi List,
in the past i could never get *plus/web/request work with uploading a file.
i can get it to work in postman, curl and powershell....but i cannot get syntax right in pxplus
i'm referring to line 100 .
does anyone know what i'm doing wrong or has used *plus/web/request to post/upload file
i dont want to use a 3rd party utility if i can help it

0040 let RAW_CREDENTIALS$="username:password"
0050 call "*web/base64;ENCODE_STR",RAW_CREDENTIALS$,BASE64_CREDENTIALS$
0060 let EXTRA_HEADERS$="Authorization: Basic "+BASE64_CREDENTIALS$
0070 let MIME_TYPE$="multipart/form-data"
0080 let URI$="https://staging.saberis.com:9000/api/v1/documents/modules/21/upload"
0100 let BODY$="filename=Cleary_Quote.xml"
0110 call "*plus/web/request",URI$,BODY$,RESPONSE$,RESPONSE_HEADER$,MIME_TYPE$,"",EXTRA_HEADERS$


thanks

jeff

Stéphane Devouard

Jeff

Attached is the PVX helper class I use to upload files to APIs

Use it like this :


field_name$="Name of the form field the webservice is waiting for"
file_name$="Name of the file to upload : this will be the value of the form field above"
file_content$="File content"
file_mime_type$="MIME type of the file to upload, application/octet-stream by default"
upload=new("http.upload",field_name$,file_name$,file_content$,file_mime_type$)
! // use upload'content_type$ as mime_type in the *plus/web/request
! // use upload'body$() as body in the *plus/web/request


The file_content$ variable should be loaded with a binary read of the file


open (hfn,isz=-1) file_name$
file_content$ = rcd(lfo,siz=10000000)


Hope this helps
Stéphane Devouard
Portfolio | Work

Mike King

Jeff,

There is a utility built into PxPlus that will load a variable with the contents of a file:  *tools/readfile.

https://manual.pvxplus.com/PXPLUS/utilities/readfile.htm

Given this all you need to do is change line 100 in your code to something like:

0100 call "*tools/readfile", "Cleary_Quote.xml", Body$
Mike King
President - BBSysco Consulting
eMail: mike.king@bbsysco.com

Jeffrey Ferreira

#3
Hi Mike, i tried your method...
of course i know i'm doing something wrong.
but i think it might have something to do with what Stephane said
the api says i have to  reference a field name and a filename

Here is there sample post below.....maybe i have to try sending this filename somewhere..
i'm going to try and find my curl command that worked and attach.

POST /api/v1/documents/modules/17/upload HTTP/1.1
Host: {host}
Authorization: Basic [BASIC_ENCODED_LOGIN_INFORMATION]=
Cache-Control: no-cache
Content-Type:https://forum1.pvxplus.com/Themes/vVide/images/bbc/bold.gif multipart/form-data; boundary=----
WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="filename"; filename="Paradigm.xml"
Content-Type: text/xml
------WebKitFormBoundary7MA4YWxkTrZu0gW--



Jeffrey Ferreira

#4
Here is my curl command that worked

curl -k  -H "Content-Type: multipart/form-data" -H "Authorization: Basic  Base64Credentials" -F file[filename]=@s:\jeff\Cleary_Quote.xml -X POST https://staging.saberis.com:9000/api/v1/documents/modules/21/upload

i'm going to try adding in what Stephane said and see if that helps.

Jeffrey Ferreira

Hi,

i got it to work  :) with Stephane's helper class.
thank you so much.
at least the response said message queued and i'm pretty sure that means it worked.
i have to read thru Stephane's class now and figure out what on earth he did.

jeff

HendersonS

Hi Jeff, I'm stuck in the same place trying to send a file via API, could you post the final solution? Maybe it can help me too!

Jeffrey Ferreira

Hi Henderson,

here was is my best try to scale down with no extra stuff
you need Stephane's program http.upload.pvc for this to work. The other stuff is standard pxplus.
if you can't get it working let me know.

0010 begin
0020 precision 4
0030 let XML_FILE$="s:\jeff\Cleary_Quote.xml" ! Your File would be different
0040 let RAW_CREDENTIALS$="login:password" ! Using Basic Credentials then separate your login and password with a colon before calling base64 program
0050 call "*web/base64;ENCODE_STR",RAW_CREDENTIALS$,BASE64_CREDENTIALS$
0060 let EXTRA_HEADERS$="Authorization: Basic "+BASE64_CREDENTIALS$
0070 let URI$="https://staging.saberis.com:9000/api/v1/documents/modules/21/upload"
0080 gosub HELPER
0090 call "*plus/web/request",URI$,BODY$,RESPONSE$,RESPONSE_HEADER$,CONTENT_TYPE$,"",EXTRA_HEADERS$
0100 print RESPONSE_HEADER$
0110 print RESPONSE$
0120 msgbox "Done"
0130 end
1000 ! ^1000
1010 HELPER:
1020 let FIELD_NAME$="filename" ! This part was custom for my api / yours will probably be different
1040 call "*tools/readfile",(XML_FILE$),FILE_CONTENTS$
1050 let FILE_MIME_TYPE$="multipart/form-data"
1060 let UPLOAD=new("http.upload",FIELD_NAME$,XML_FILE$,FILE_CONTENTS$,FILE_MIME_TYPE$) ! This is Stephane's program http.upload.pvc that handles the boundaries (this is part i was messing up (the boundaries))
1070 let CONTENT_TYPE$=UPLOAD'CONTENT_TYPE$
1080 let BODY$=UPLOAD'BODY$()
1090 return

HendersonS

Jeff thanks for your help,

I have something very similar to what you showed, I also added Stephane's program to calculate the boundaries but when I send it with pxplus the API returns an "invalid file" error, I tried it with CURL and the api receives the file correctly, does it work? How could I replicate it? in Pxplus?

Example with CURL: curl -X 'POST' \
  'https://url/api' \
  -H 'accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -F 'xml=@xmldata.xml;type=text/xml'


Jeffrey Ferreira

Henderson, for me the problem was that keyword file/filename that i had to pass in.
i feel like you might have to pass in the word "xml"
i could be wrong.
do me a favor - you know where i have
1080 let BODY$=UPLOAD'BODY$() ! Calling Stephane's code
after you execute this method
print the mid(body$,1,300) and then send the contents to this thread...

HendersonS

jeff, this is what it returns:

1>print mid(body$,1,300)
------PxPlusFormDataBoundary_zNYKbtQdhRIYGlYIDBoKNPAdi7un9gGR
Content-Disposition: form-data; name="xmlsing"; filename="xmlsing.xml"
Content-Type: text/xml

<?xml version="1.0" encoding="utf-8"?>
<SemillaModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="h
ttp://www.w3.
1>

Jeffrey Ferreira

i think the problem is this line
Content-Disposition: form-data; name="xmlsing"; filename="xmlsing.xml"

Mine looks like this
Content-Disposition: form-data; name="filename"; filename="s:\jeff\Cleary_Quote.xml"

i feel like your line should like something like this:

Content-Disposition: form-data; name="filename"; filename="xmlsing.xml"
in other words
the value that name =  points to should be the key in the next key value pair
i have to head out but i'll try to check back in again later on this.
                                                           

HendersonS

#12
jeff,

I tested both ways that you showed me and I still get the same "invalid file" response, I will continue trying...

Stéphane Devouard

Henderson,

Jeff's suggestion is only an example, you need to check the documentation of the API you're trying to interact with and see what is the name of the html form field that this API expects. If filename does not work, maybe it is file_name or xml_file.
Also make sure that the data in the XML file is escaped using either cvs(..., "utf8:xml") if the data may contain utf8 encoded characters, or cvs(..., "ascii:xml") in case the data contains reserved XML chars such as < >


Jeff,

You can spare a call by using the cvs() function
base64_credentials$ = cvs(raw_credentials$, "ascii:base64")
Stéphane Devouard
Portfolio | Work

HendersonS

Hi,
Jeff and Stéphane, thank you for your help, I was finally able to find the solution with the help you gave me.

the line looked like this:
Content-Disposition: form-data; name="xml"; filename="xmlsing.xml"