Skip to Content
0

HANA XS: uploading binary attachments to the Business One Service Layer

Dec 07, 2017 at 11:13 AM

108

avatar image

Hi everyone,

I've run into an issue with uploading binary attachments to the Business One Service Layer through HANA XS.

The business case goes as follows. The customer wants us to build an API in HANA XS. The API sits between a third party application and the Business One Service Layer. One of the endpoints should make it possible to create service calls, and add attachments to these service calls. The attachments are sent as BASE64 strings in the following format:

{
    "businessPartnerCode": "C12345",
    "comments": "Please look into this",
    ...
    "attachments" : [
        {
        "filename": "document.pdf",
        "mimeType": "application/pdf",
        "contents": "iVBORw0KGgoAAAANSUhEUgAAAMMAAADD…"
        }
    ]
}

Creating the service call works without an issue.

After creating the service call I want to create the attachments, and then later on link them to the service call.

In the documentation of the Service Layer ("Working with SAP Business One Service Layer") on page 68 it details how attachments can be uploaded by sending them in a multipart request: "Attach source file from Windows".

When I perform a POST to the Service Layer endpoint for this (/b1s/v1/Attachments2) with Postman, I do the following:

This works as expected:

{
    "odata.metadata": "https://***:50000/b1s/v1/$metadata#Attachments2/@Element",
    "AbsoluteEntry": "11533",
    "Attachments2_Lines": [
        {
            "SourcePath": "/tmp/sap_b1_b1service0/ServiceLayer/Attachments2/",
            "FileName": "sap",
            "FileExtension": "png",
            "AttachmentDate": "2017-12-05",
            "UserID": "1",
            "Override": "tNO",
            "U_CorCategory": null
        }
    ]
}

The file is created, and the entry is written to the ATC1 table of Business One.

However, when I try to perform the same thing in HANA XS, I keep getting an Internal Server Error, or just no result at all.

This is the body of my method which executes the request. I added comments to specify what each other method call does, and what the end result is:

{
    var client = new $.net.http.Client();
    // The client is instantiated correctly, and works for other calls
        
    var request = new $.web.WebRequest(this.determineHttpMethod(httpVerb), querystring);
    // - The request is instantiated correctly, and works for other calls
    // - The http method is set to $.net.http.POST
    // - querystring contains the path: /b1s/v1/Attachments2


    this.setRequestHeaders(httpVerb, request, headers);
    // - Ability to add additional headers to the request
    // - None are specified


    this.setRequestBody(requestBody, true, request);
    // requestBody is undefined, and thus will not bet set


    this.setRequestCookies(request);
    // - This method will add the B1SESSION and ROUTEID cookies to the requests, which were received from the login call earlier in the class
    // - Login and logout works perfectly, and other calls to the Service Layer also work correctly


    this.addAttachments(attachments, request);
    // Detailed below


    client.request(request, this.readDestination());
    // The destination is correctly parsed


    var response = client.getResponse();
    // response is an empty object for the attachments call


    this.storeResponseCookies(response);
    // Store the B1SESSION and ROUTEID cookies for successive calls


    return this.formatResponse(querystring, response);
    // Format the response, and send it back
}

The addAttachments method looks like this:

{
    if (attachments == null || !Array.isArray(attachments)) {
        return;
    }


    this.setMultipart(request);
    // Override the Content-Type header of the request itself by setting it to multipart/form-data 


    for (var attachmentKey in attachments) {
        var attachment = attachments[attachmentKey];


        var entity = request.entities.create();


        var cid = moment().toDate().getTime().toString();
        // - Get the milliseconds since the Unix epoch with Moment.js
        // - Works as expected


        var contents = $.util.codec.decodeBase64(attachment.contents);
        // - Decode the base64 string into an ArrayBuffer
        // - I decoded the base64 string also on my machine, and it’s a correct representation of the binary file
        // - I checked the byteLength property to make sure the ArrayBuffer is created correctly


        request.parameters.set("content['" + attachment.filename + "']", cid);
        // Link the content and CID


        entity.headers.set('Content-Disposition', 'form-data; name="files[\'' + attachment.filename + '\']"; filename="' + attachment.filename + '"');
        // - Set the Content-Disposition header
        // - The header is set correctly


        entity.headers.set('Content-Type', attachment.contentType);
        // The mime-type of the attachment, which is passed correctly


        entity.setBody(contents);
        // Set the ArrayBuffer as the body of the entity
    }
}

As you can see, there is nothing overly complex going on here, but I don’t know why it works with Postman, but not with XS.

The addAttachments method runs correctly. I traced the output of every single line. The request itself is sent, but it seems it’s somehow refused by the Service Layer. I played with different mimetypes, one attachment, multiple attachments, additional headers,…

I suspect it has something to do with the boundaries, which are set automatically with Postman. But I can't look into the raw request, so I'm unable to verify this.

postman-1.png (29.3 kB)
postman-2.png (27.4 kB)
10 |10000 characters needed characters left characters exceeded
* Please Login or Register to Answer, Follow or Comment.

1 Answer

Ralph Oliveira
Jan 05 at 09:51 AM
0

Hi Bert, just to formalize what we have already discussed.

Your code seems ok and apparently the XS Engine isn't manipulating correctly the multipart request.

Support still investigating the case and should provide an answer on your ticket (please share it here once you have it)

Show 3 Share
10 |10000 characters needed characters left characters exceeded

Will do Ralph. Thanks for the follow-up!

0

Hi Bert, hi Ralph,
are there still any news? :-)

0

Bert ended up with another approach (Not using HANA XS) to upload files but the case stills with support. :(

You can also open your own incident to shine more light into this issue. The more use cases the more urgent it becomes.

0