httpRequest POST file Upload multipart
httpRequest POST file Upload multipart
1. is there a code example step-by-step, how to build each part ?
I tried the demo from intersystems doc's,
I checked it in .Net response, that didnot recognize the File-data part
2. I noticed, there is :
SET BinaryMIMEPart.ContentType="application/octet-stream"
but missing :
SET BinaryMIMEPart.ContentDisposition (as analogue, let say for VBA EXCEL )
or I do it via the:
Do BinaryMIMEPart.SetHeader("Content-Disposition",...) ?
that is not analouge for VBA
need any code example link, to execute correctly all the multipart (cache call it MIMEtype)
settings
I had the same challenges as you when I was tackling this - documentation wasn't really fleshing it out well enough. ISC Sales Engineer helped me work through it.
Here is what I have used with success to submit both an XML doc and PDF doc to a vendor along with two parameters associated with the request (ReportId and CustomerId.) Requires use of MIME Parts. I hope this helps you. I had to genericize some of the code to share but it is commented by me what each part does and how it pulls together.
Note this assumes you're passing in a variable pRequest that is a message class that holds your data. Also I am running this on 2019.1, not 2018 so not sure of the differences when using things like %JSONImport (may be none but I don't know that for certain.)
Set tURL = "fully_qualified_endpoint_url_here" // Instantiate reportId MIME Part Set reportId = ##class(%Net.MIMEPart).%New() // Define/Set the Content-Disposition header indicating how this MIME part is encoded and what it contains. // Final string looks like: form-data; name="reportId" S tContentDisp1 = "form-data; name="_$CHAR(34)_"reportId"_$CHAR(34) Do reportId.SetHeader("Content-Disposition", tContentDisp1) // Get the ReportId from the incoming Request (from BPL) and write to the MIME Part body. S tReportId = pRequest.ReportId Set reportId.Body = ##class(%GlobalCharacterStream).%New() Do reportId.Body.Write(tReportId) // Instantiate customerId MIME Part Set customerId = ##class(%Net.MIMEPart).%New() // Define/Set the Content-Disposition header indicating how this MIME part is encoded and what it contains. // Final string looks like: form-data; name="customerId" S tContentDisp2 = "form-data; name="_$CHAR(34)_"customerId"_$CHAR(34) Do customerId.SetHeader("Content-Disposition", tContentDisp2) // Get the CustomerId from the incoming Request (from BPL) and write to the MIME Part body. S tCustomerId = pRequest.CustomerId Set customerId.Body = ##class(%GlobalCharacterStream).%New() Do customerId.Body.Write(tCustomerId) // Instantiate file1 (XML Structured Doc) MIME Part Set file1 = ##class(%Net.MIMEPart).%New() // Define/Set the Content-Disposition header indicating how this MIME part is encoded and what it contains. // Final string looks like: form-data; name="file1"; filename="<pRequest.CaseNumber>.xml" S tXmlFileName = pRequest.CaseNumber_".xml" S tContentDisp3 = "form-data; name="_$CHAR(34)_"file1"_$CHAR(34)_"; filename="_$CHAR(34)_tXmlFileName_$CHAR(34) Do file1.SetHeader("Content-Disposition", tContentDisp3) // Get the XML as a Stream from the incoming Request (from BPL) and write to the MIME Part body. Set tStream = ##class(%GlobalCharacterStream).%New() Set tSC = pRequest.XmlDoc.OutputToLibraryStream(tStream) Set file1.Body = tStream Set file1.ContentType = "application/xml" // Instantiate file1 (PDF Report) MIME Part Set file2 = ##class(%Net.MIMEPart).%New() // Define/Set the Content-Disposition header indicating how this MIME part is encoded and what it contains. // Final string looks like: form-data; name="file1"; filename="<pRequest.CaseNumber>.xml" S tPdfFileName = pRequest.CaseNumber_".pdf" S tContentDisp4 = "form-data; name="_$CHAR(34)_"file2"_$CHAR(34)_"; filename="_$CHAR(34)_tPdfFileName_$CHAR(34) Do file2.SetHeader("Content-Disposition", tContentDisp4) // Get the PDF Stream from the incoming Request (from BPL) and write to the MIME Part body. Set file2.Body = pRequest.PdfDoc.Stream Set file2.ContentType = "application/pdf" // Package sub-MIME Parts into Root MIME Part Set rootMIME = ##class(%Net.MIMEPart).%New() Do rootMIME.Parts.Insert(reportId) Do rootMIME.Parts.Insert(customerId) Do rootMIME.Parts.Insert(file1) Do rootMIME.Parts.Insert(file2) // Write out Root MIME Element (containing sub-MIME parts) to HTTP Request Body. Set writer = ##class(%Net.MIMEWriter).%New() Set sc = writer.OutputToStream(..%HttpRequest.EntityBody) if $$$ISERR(sc) {do $SYSTEM.Status.DisplayError(sc) Quit} Set sc = writer.WriteMIMEBody(rootMIME) if $$$ISERR(sc) {do $SYSTEM.Status.DisplayError(sc) Quit} // Set the HTTP Request Headers // Specify the Authorization header containing the OAuth2 Bearer Access Token. Set tToken = "set your token here or pull from wherever" Set tSC = ..%HttpRequest.SetHeader("Authorization","Bearer "_tToken) // Specify the Content-Type and Root MIME Part Boundary (required for multipart/form-data encoding.) Set tContentType = "multipart/form-data; boundary="_rootMIME.Boundary Set tSC = ..%HttpRequest.SetHeader("Content-Type",tContentType) // Call SendFormDataArray method in the adapter to execute POST. Response contained in tHttpResponse Set tSC=..Adapter.SendFormDataArray(.tHttpResponse,"POST", ..%HttpRequest, "", "", tURL) // Validate that the call succeeded and returned a response. If not, throw error. If $$$ISERR(tSC)&&$IsObject(tHttpResponse)&&$IsObject(tHttpResponse.Data)&&tHttpResponse.Data.Size { Set tSC = $$$ERROR($$$EnsErrGeneral,$$$StatusDisplayString(tSC)_":"_tHttpResponse.Data.Read()) } Quit:$$$ISERR(tSC) If $IsObject(tHttpResponse) { // Instantiate the response object S pResponse = ##class(Sample.Messages.Response.VendorResponseMsgClass).%New() // Convert JSON Response Payload into a Response Object S tSC = pResponse.%JSONImport(tHttpResponse.Data) }
Hi Craig,
I followed your code, there some obstacles, that I cannot implement your example :
1. I run the from standard Module.int (not class)
2. what is the syntax : ..%HttpRequest.SetHeader(...) ?
3. what is : ..Adapter.SendFormDataArray(.tHttpResponse,"POST", ..%HttpRequest, "", "", tURL) ?
what the ..Adapter means in standard module ?
I used just :
Set RootMIMEPart=##class(%Net.MIMEPart).%New()
Set BinaryMIMEPart=##class(%Net.MIMEPart).%New()
Set contentDisp=contentDisp_"form-data; name="_Q_"fileUpload"_Q
BPT> Set contentDisp=contentDisp_"; filename="_Q_file_Q
SET contentDisp=contentDisp_"; filename="_Q_file_Q
Set BinaryMIMEPart.ContentDisposition=contentDisp
Do BinaryMIMEPart.SetHeader("Content-Disposition",contentDisp)
Set BinaryMIMEPart.ContentType="application/octet-stream"
and so on...
using standard objects
I'm not really clear on what you mean by "standard Module.int" so sounds like we may be approaching this in different ways and I apologize for any confusion I caused.
%HttpRequest is %Net.HttpRequest (you can find syntax for SetHeader here) and the Adapter in this case refers to the adapter attached to the EnsLib.REST.Operation class via Parameter, which in this case is EnsLib.HTTP.OutboundAdapter.
Emanuel,
Since you're writing this in a normal .int, you won't want to use "..%HTTPRequest". You can just instantiate a new request object and name it something like httpRequest:
And instead of Craig's ..Adapter call, you can use httpRequest's Post method.
tried multi Part form with one file.pdf to own .Net listener
the "POST" did not arrive to "web server" ?
my example (took from craig, with some corrections) :
on :
httpRequest.SendFormDataArray...
got error, so I tried .POST
code example :
HttpMimeRequest ;
S Q=$C(34)
S HOST = "127.0.0.1" ;"localhost"
S PORT = "8080"
Set httpRequest=##class(%Net.HttpRequest).%New()
Set httpRequest.Server=HOST
Set httpRequest.Port = PORT
// Create root MIMEPart
Set RootMIMEPart=##class(%Net.MIMEPart).%New()
//Create binary subpart and insert file data
Set BinaryMIMEPart=##class(%Net.MIMEPart).%New()
Set contentDisp=""
Set contentDisp=contentDisp_"form-data; name="_Q_"fileUpload"_Q
Set contentDisp=contentDisp_"; filename="_Q_file_Q
;Set BinaryMIMEPart.ContentDisposition=contentDisp
Do BinaryMIMEPart.SetHeader("Content-Disposition",contentDisp)
Set BinaryMIMEPart.ContentType="application/pdf" ;"application/octet-stream"
Set ContentType= "multipart/form-data; boundary="_RootMIMEPart.Boundary
Set httpRequest.ContentType=ContentType
// do we need it : ?
Do BinaryMIMEPart.SetHeader("Content-Type", ContentType)
Set stream=##class(%FileBinaryStream).%New()
Set stream.Filename=file
Do stream.LinkToFile(file)
Set BinaryMIMEPart.Body=stream
// Insert both subparts into the root part
Do RootMIMEPart.Parts.Insert(BinaryMIMEPart)
// create MIME writer; write root MIME message
Set writer=##class(%Net.MIMEWriter).%New()
// Prepare outputting to the HttpRequestStream
Set status=writer.OutputToStream(httpRequest.EntityBody)
if ('status) {do $SYSTEM.Status.DisplayError(status) Quit}
// Now write down the content
Set status=writer.WriteMIMEBody(RootMIMEPart)
if ('status) {do $SYSTEM.Status.DisplayError(status) Quit}
// QueryString Example ;
Set jq = " index.htm?CUSTID=myCust"
//set url="alfresco/service/sample/upload.json?"
// _"alf_ticket=TICKET_caee62bf36f0ea5bd51194fce161f99092b75f62"
;set url="http://localhost:8080"
;set status=httpRequest.Post(url,0)
;if ('status) {do $SYSTEM.Status.DisplayError(status) Quit}
P1 ;
;Set url = HOST_"http://"_HOST_":"_PORT_"/"_jq
;Set tSC = httpRequest.SendFormDataArray(.httpResponse,"POST", httpRequest, "", "", url)
Set rc=httpRequest.Post(jq)
//
What error did you receive when you used SendFormDataArray?
Hi Marc,
1.
P1 ;
Set url = HOST_"http://"_HOST_":"_PORT_"/"_jq
Set tSC = httpRequest.SendFormDataArray(.httpResponse,"POST", httpRequest, "", "", url)
//Set rc=httpRequest.Post(jq)
the error I get:
<METHOD DOES NOT EXIST>P1+2^HTTP3 *SendFormDataArray,%Net.HttpRequest
2. I asked berfore:
in what Part I set contentDisposition ?
I mean not in SetHeaders, but as another part in Body ?
or, I don't need it ?
1) Ah, that makes sense. SendFormDataArray is a method in EnsLib.HTTP.OutboundAdapter, but you're not using an adapter.
To do a POST using %Net.HTTPRequest, you'll need to use the Post() method.
2) I had a look at the HTTP specs, and it looks like content-disposition is required for each part:
multipart/form-data
body, the HTTPContent-Disposition
general header is a header that must be used on each subpart of a multipart body to give information about the field it applies to. The subpart is delimited by the boundary defined in theContent-Type
header. Used on the body itself,Content-Disposition
has no effect.You can set this like any other header using the SetHeader method in %Net.MIMEPart. You're already doing that here:
Here's a working example based on Craig's original sample. It sends a request with 3 mime parts: a form variable with a value, an XML file, and a PDF file loaded from disk.
Hi Marc,
1. I built ASP.NET some "server" http listener with [HttpPost] controller
2. I activate your code meanwhile on 1 file (.pdf)
with URL ...serverAsp/postfile/ ...
and got correctly the "file" from your POST() code, means in .NET:
var request = System.Web.HttpContext.Current.Request;
var files = request.Files.Count;
---> files.Count = 1
so .NET server recognized multi-part structure with 1 file !
4. I saw you made little changes to Craig code, and made it more simpler with just .Post(Url), without any "adapters"... and nor .SendFormDataArray...
that did not work for me in the beginning.
5. I'll made more testing , to see if the whole file is transfered correctly
but its seems to work.
thanks,
Hi Marc,
I have another Topic, that did not get an answer (from26/08/2020) ,
can you help about, or notice the Owner ? :
Dmitriy,
1. I tried the open socket script, you wrote, and it works OK.
2.now I want to send some string messages on the opened socket,
how I do it ?
I have to add on each "MyMessage" string some header before ?
can you write exact script how I implement using send message(s), and handle get response on each
message.
did not find any docs about