Sidebar

File Upload to API with Webservice

0 votes
571 views
asked Mar 1, 2023 by raymond-l-7816 (150 points)
edited Mar 1, 2023 by raymond-l-7816

Hi everyone, 

I was recently trying to upload files(pdf, image files like jpg, png) to a 3rd party APIs (PCC, FYI). 

With Postman, the file uploading was successful. However, when I translated that to Qvera code, it always failed with an error: "Required request part 'metadata' is not present"

I am attacheing the raw request and also the Qvera code here for your reference. If you experienced something similar or you can tell the root cause of the problem, would you please share your knowledge and help me out? Thanks a lot!

 

This is the raw request of Postman (I got a 201 success response):

 

POST /api/public/preview1/orgs/xxxxxxx/patients/####/documents HTTP/1.1

Content-Type: multipart/form-data; boundary=--------------------------047322903399446535512509

Accept: application/json

Authorization: Bearer xxxxxxxxxx:2

User-Agent: PostmanRuntime/7.31.0

Postman-Token: zzzzzzzzzz

Host: connect2.pointclickcare.com

Accept-Encoding: gzip, deflate, br

Connection: keep-alive

Cookie: JSESSIONID=xxxxxxxxxxxx

Content-Length: 42186

 

----------------------------047322903399446535512509

Content-Disposition: form-data; name="file"; filename="pn.pdf"

<pn.pdf>

----------------------------047322903399446535512509

Content-Disposition: form-data; name="metadata"; filename="metadata.json"

<metadata.json>

----------------------------047322903399446535512509--

 

The Qvera code is straightforward:

 

// Set up the boundary; this will be used to separate the different

// parts of the multipart message, as well as denote start and end

var boundary = qie.getUUID(true).substring(0,10);

 

// Read a detailed medical file

var fileBytes = qie.readFile('C:\\PatientDocuments\\pn.pdf');

 

var metadataFileBytes = qie.readFile('C:\\PatientDocuments\\metadata.json');

 

// Set up the parameter map

var parameterMap = qie.newParameterMap();

 

// If sending binary data, encoding must be set to

// ISO-8559-1; if set to UTF-8, the data will be

// munged and unusable

parameterMap.put('http.header.encoding', 'ISO-8859-1');

 

var baos = new java.io.ByteArrayOutputStream();

// Create your multipart content. Each item in the multipart must

// be surrounded by the boundary

 

var metadataPart = new java.lang.String(

'\r\n' +

'--------------------------' + boundary + '\r\n' +

'Content-Disposition: form-data; name="metadata"; filename="metadata.json"\r\n' +

'Content-Type: application/json\r\n' +

'\r\n').getBytes();

 

baos.write(metadataPart, 0, metadataPart.length);

baos.write(metadataFileBytes, 0, metadataFileBytes.length);


 

var part = new java.lang.String(

'\r\n\r\n' +

'--------------------------' + boundary + '\r\n' +

'Content-Type: application/pdf\r\n' +

'Content-Disposition: form-data; name="file"; filename="pn.pdf"\r\n' +

'\r\n').getBytes();

 

baos.write(part, 0, part.length);

baos.write(fileBytes, 0, fileBytes.length);


 

var endingBoundary = new java.lang.String(

'\r\n\r\n--------------------------' + boundary + '--').getBytes();

 

baos.write(endingBoundary, 0, endingBoundary.length);


 

var content = new java.lang.String(baos.toByteArray(), 'ISO-8859-1');

 

var urlTemplate = qie.evaluateTemplate(qie.getWsEndpointUrl("PCC-patient-document-upload"));

var value = null;

try {

value = qie.callRESTWebService(

"PCC-patient-document-upload",

urlTemplate,

"POST",

content,

'multipart/form-data; boundary=--------------------------' + boundary,

parameterMap,

60000);

} catch(e){

qie.error(e);

}

 

The raw request that generated by the Qvera code (unfortunately, got a 400 error complaining the missing required 'metadata'):

 

URL: https://connect2.pointclickcare.com/api/public/preview1/orgs/xxxxxxxxxxx/patients/####/documents
Request Method: POST
Call Creation Time: 0ms

Headers:
Accept: application/json
Authorization: Bearer xxxxxxxxxx:2
Content-Length: 42113
Host: connect2.pointclickcare.com
Content-Type: multipart/form-data; boundary=--------------------------76b895c4a5; charset=ISO-8859-1


--------------------------76b895c4a5
Content-Disposition: form-data; name="metadata"; filename="metadata.json"
Content-Type: application/json

{
    "documentName": "pn.pdf",
    "documentCategory": 298,
    "effectiveDate": "2023-02-22T14:18:00.000Z"
}

--------------------------76b895c4a5
Content-Disposition: form-data; name="file"; filename="pn.pdf"
Content-Type: application/pdf

<note: the pdf file string is too long to be attached here>

--------------------------76b895c4a5--

1 Answer

0 votes
 
Best answer

I was able to make a call from postman with two files (a PDF test file and a JSON file) to mimic your request.  I made a few changes to the script to make sure that the two requests are exactly the same.

// Set up the boundary; this will be used to separate the different
// parts of the multipart message, as well as denote start and end
// postman sets the boundary to 26 dashes, then a 24 digit number, we will use the same
var boundary = '--------------------------' + qie.getUUID(true).substring(0,24);

// Read a detailed medical file
var fileBytes = qie.readFile('C:\\tmp\\pn.pdf');
var metadataFileBytes = qie.readFile('C:\\tmp\\metadata.json');

// Set up the parameter map
var parameterMap = qie.newParameterMap();

// If sending binary data, encoding must be set to
// ISO-8559-1; if set to UTF-8, the data will be
// munged and unusable
parameterMap.put('http.header.encoding', 'ISO-8859-1');
// we are encoding the string as "ISO-8859-1" to avoid messing with binary bytes, but we don't need to send that in the Content-Type header, so we will override the header with our own content-type removing the encoding (Postman does not send the encoding)
parameterMap.put('http.header.Content-Type', 'multipart/form-data; boundary=' + boundary);

// Only to make the payload the same as postman, I changed the order so that the PDF gets sent first, then the JSON

var baos = new java.io.ByteArrayOutputStream();
// Create your multipart content. Each item in the multipart must
// be surrounded by the boundary

var part = new java.lang.String(
// Everytime a boundary is sent, postman adds two dashes, so it actually has 28 dashes, then 24 digits, we will make it the same, Aslo, we had an extra line above the boundary, so I removed it.
   '--' + boundary + '\r\n' +
   'Content-Disposition: form-data; name="file"; filename="pn.pdf"\r\n' +
   'Content-Type: application/pdf\r\n' +
   '\r\n').getBytes();

baos.write(part, 0, part.length);
baos.write(fileBytes, 0, fileBytes.length);


var metadataPart = new java.lang.String(
// Everytime a boundary is sent, postman adds two dashes, so it actually has 28 dashes, then 24 digits, we will make it the same, Aslo, we had an extra line above the boundary, so I removed it.
   '\r\n' +
   '--' + boundary + '\r\n' +
   'Content-Disposition: form-data; name="metadata"; filename="metadata.json"\r\n' +
   'Content-Type: application/json\r\n' +
   '\r\n').getBytes();

baos.write(metadataPart, 0, metadataPart.length);
baos.write(metadataFileBytes, 0, metadataFileBytes.length);


var endingBoundary = new java.lang.String(
// Everytime a boundary is sent, postman adds two dashes, so it actually has 28 dashes, then 24 digits, we will make it the same, Aslo, we had an extra line above the boundary, so I removed it, we also needed to end the last boundary with two dashes and a line ending.
   '\r\n--' + boundary + '--\r\n').getBytes();

baos.write(endingBoundary, 0, endingBoundary.length);


var content = new java.lang.String(baos.toByteArray(), 'ISO-8859-1');

var urlTemplate = qie.evaluateTemplate("http://localhost:20021/testing");
var value = null;
try {
value = qie.callRESTWebService(
   "Test REST",
   urlTemplate,
   "POST",
   content,
   'multipart/form-data; boundary=' + boundary,
   parameterMap,
   60000);
} catch (e) {
qie.error(e);
}
 

answered Mar 1, 2023 by ben-s-7515 (12,640 points)
selected Mar 2, 2023 by raymond-l-7816
...