Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
daniel_paulsen
Active Contributor

A new RESTful API introduced with the release of SAP BusinessObjects Business Intelligence Platform 4.2 allows for uploading and downloading agnostic documents to/from the BI Platform repository.


The purpose of this blog is to describe the basic requirements for uploading files and to provide some sample code to help with getting started.


When uploading a document, sending the file to the request stream requires some very specific formatting that can be frustrating if you need to construct the full request manually, or it can be very easy if you have tools or classes that take care of the formatting for you.  I will cover 3 scenarios to hopefully demonstrate this:

  1. Using a REST client – Easy
  2. Using .NET to construct the request from scratch – Tricky
  3. Using the Java class MultipartEntityBuilder to build the request – Moderately easy.
  4. Using Javascript. - Moderately easy.



Uploading a file over http requires that a request be sent using a Content-Type of multipart/form-data (RFC2388).  This means that the request sent to the server is made up of a series of parts separated by a boundary.  The boundary is content that is unique in the body of the data being sent. When uploading a file to the request stream, the stream should be constructed as follows:

    • Two dashes.
    • The boundary (a unique string that does not occur in the data).
    • One CRLF (\r\n).
    • A content-disposition header with the name of the form field corresponding to the file and the name of the file. That looks like: Content-Disposition: form-data; name="formfieldname"; filename="mydocument.pdf"
    • One CRLF (\r\n).
    • A content-type header describing the MIME type of the file. For example: Content-Type: application/pdf
    • Two CRLFs (\r\n\r\n). An empty line must exist between the form data and the file contents.
    • The entire contents of the file, byte for byte.
    • One CRLF (\r\n).
    • Two dashes + The boundary + Two dashes (the trailing two dashes represent the closing of the boundary)
    • One CRLF (\r\n).

Uploading a file using a REST client.

Rest clients provide an easy way to test RESTful calls without having to write any code.  For this example, The Postman Rest client (downloaded from the Chrome web store) is used to upload a file.  Additionally, a tool to monitor HTTP traffic is also used. This tool is Fiddler.

To upload an agnostic file, use the following API :

POST   http://<serverName>:6405/biprws/infostore/folder/<folderId>/file

<serverName> is the name of the machine hosting the Web Application Container Server (WACS)

<folderId> is the SI_ID of the folder in the CMS repository that you are uploading the file to.

Headers:

                X-SAP-LogonToken : “<yourLogonToken>”

The below Postman screenshot shows that we are using a POST to upload a file to a folder with the SI_ID of 8835

For the Body of the request

  • Select “form-data”
  • Provide a name corresponding to the file being uploaded (this is used in the Content-Disposition header)
  • Browse to the file you wish to upload
  • Click “Send”

A response with the title, cuid, id and parentID is returned in the following format.

Note we did not have to worry about the content-type of the file, the boundary separator etc.  This is all handled for you by the REST client. You can see that the multipart/form-data format (described above) was used by looking at the request in Fiddler:

POST http://bipw08r2:6405/biprws/infostore/folder/8835/file HTTP/1.1

Host: bipw08r2:6405

Connection: keep-alive

Content-Length: 85463

X-SAP-LogonToken: "BIPW08R2:6400@{3&2=10032,U3&2v=BIPW08R2:6400 … KBdq7C65TRllA,UP}"

Cache-Control: no-cache

Origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop

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

User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36

Postman-Token: 8d1983b3-afbb-c895-1e5e-f7939b842ed4

Accept: */*

Accept-Encoding: gzip, deflate

Accept-Language: en-US,en;q=0.8

------WebKitFormBoundaryAOQ9N10uh0pJmNxr

Content-Disposition: form-data; name="uploadFileTest"; filename="testUpload.pdf"

Content-Type: application/pdf


<pages of binary data>

------WebKitFormBoundaryAOQ9N10uh0pJmNxr--

Uploading a file using .NET


.NET provides a class called MultipartFormDataStreamProvider to use for HTML file uploads, however for the purpose demonstrating how to construct a request from scratch, it will not be used in this example.  The Sample code below contains comments to describe each step.  While it is not complicated, it demonstrates the “tricky” part mentioned earlier, which is ensuring that carriage returns are inserted in key parts of the request, or it will fail.


        private void uploadAgnosticFile(String uploadURL, string rwsToken, string contentType, string filePath)
        {
            /************************************************************************************
             * Parameters:
             *
             * uploadURL   …/biprws/infostore/folder/<folderId>/file
             * rwsToken the logonToken contained inside of double quotes
             * contentType The Mimetype of the file to be uploaded
             * filePath the full path to the file including the filename and extension
             *
             ***********************************************************************************/
            string fileName = filePath.Substring(filePath.LastIndexOf("\\")+1);
            //Create the unique boundary
            String boundary = "--" + System.Guid.NewGuid().ToString();
            byte[] boundaryBytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
            byte[] boundaryCloseBytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
            //Create the Web request
            HttpWebRequest myWebRequest = (HttpWebRequest)WebRequest.Create(uploadURL);
            myWebRequest.ContentType = "multipart/form-data; boundary=" + boundary;
            myWebRequest.Method = "POST";
            myWebRequest.Headers.Add("X-SAP-LogonToken", rwsToken);
            myWebRequest.Accept = "*";
            //Construct the form data
            string myFormData = string.Empty;
            myFormData = "Content-Disposition: form-data; name=\"myuploadfile\"; filename=\"" + fileName + "\"\r\n";  //fileName is the name shown in launchpad
            myFormData += "Content-Type: " + contentType + "\r\n\r\n";          // must have an extra blank line between the form data and the filestream or a zero byte file is uploaded
            //convert the string myFormData to an array of bytes
            byte[] formDataBytes = System.Text.Encoding.ASCII.GetBytes(myFormData);
            // Read the file to upload as a stream
            FileStream fStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            int fileSize = (int)fStream.Length;
            byte[] filebytes = new Byte[fileSize];
            fStream.Read(filebytes, 0, fileSize);
            fStream.Close();
            // add it all together into the request stream.
            Stream reqStream = myWebRequest.GetRequestStream();
            reqStream.Write(boundaryBytes, 0, boundaryBytes.Length);            //boundary
            reqStream.Write(formDataBytes, 0, formDataBytes.Length);            //form data with Content-Disposition
            reqStream.Write(filebytes, 0, fileSize);                            //file as a stream of bytes
            reqStream.Write(boundaryCloseBytes, 0, boundaryCloseBytes.Length);  //boundary with closing dashes.
            reqStream.Close();
            string output = string.Empty;
            try
            {
                WebResponse myWebResponse = (WebResponse)myWebRequest.GetResponse();
                StreamReader sr = new StreamReader(myWebResponse.GetResponseStream());
               output = sr.ReadToEnd();
                sr.Close();
                Response.Write(output);
             }
            catch (WebException ex)
            {
                Response.Write("Error getting response.  Response Code: " + ex.Message.ToString());
            }
            catch (Exception ex)
            {
                Response.Write("general error: " + ex.Message.ToString());
            }
        }





Uploading a file with Java


For uploading a file using Java, the MultipartEntityBuilder class is used in the below code. Required jars are httpclient-4.5.1.jar and httpmime-4.5.1.jar.


private static String parentFolderId = "8835";
private static String rwsToken = null;
private static  File fileToUpload = new File("c:\\testfiles\\testupload.pdf");
private static String baseURL = "http://<serverName>:6405/biprws/";
private static String logonURL = baseURL + "logon/long";
private static String infostoreURL = baseURL + "infostore/";
private static String uploadURL = infostoreURL + "folder/" + parentFolderId + "/file";
rwsToken = getToken(userName,password,auth);
HttpPost httpPost = null;
CloseableHttpClient httpClient = null;
HttpResponse httpResponse = null;
MultipartEntityBuilder multipartEntityBuilder = null;
FileBody fileBody = new FileBody(fileToUpload);
multipartEntityBuilder = MultipartEntityBuilder.create();
multipartEntityBuilder.setCharset(Charset.forName("UTF-8"));
multipartEntityBuilder.addPart("File",fileBody);
httpPost = new HttpPost(uploadURL);
httpPost.setHeader("X-SAP-LogonToken", rwsToken);
httpPost.setEntity(multipartEntityBuilder.build());
httpClient = HttpClientBuilder.create().build();
httpResponse = httpClient.execute(httpPost);







Uploading a file with Javascript


The following html page demonstrates how to log on and upload a file using Javascript.


<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body >
<input id="File1" type="file" />
<input id="Button1" type="button" value="upload file" onclick="uploadtoCMS()" />
<script type="text/javascript">
function uploadtoCMS()
{
         // debugger;
    // Logs into the platform
    var logon = new XMLHttpRequest();
    var url = 'http://bipw08r2:6405/biprws/logon/long';
   
    var body = '<?xml version="1.0"?><attrs xmlns="http://www.sap.com/rws/bip"><attr name="userName" type="string">user_name</attr><attr name="password" type="string">your_password</attr><attr name="auth" type="string" possibilities="secEnterprise,secLDAP,secWinAD">secEnterprise</attr></attrs>';
    var logonToken;
    logon.open('POST', url, false);
    logon.setRequestHeader('Content-Type', 'application/xml');
    logon.setRequestHeader('Accept', 'application/xml');
    logon.send(body);
    //retrieve logon token from response
    logonToken = logon.getResponseHeader('X-SAP-LogonToken');
    //uploads file to CMS
    var fileToUpload = document.getElementById("File1").files[0];
    var uploadRequest = new XMLHttpRequest();
    var fd = new FormData();
    var uploadURL = 'http://<servername>:6405/biprws/infostore/folder/<folderId>/file';
    fd.append('uploadtest', fileToUpload);
    uploadRequest.open('POST', uploadURL, false);
    uploadRequest.setRequestHeader('X-SAP-LogonToken', logonToken);
    uploadRequest.send(fd);
    document.write("The following file was uploaded to the CMS: " + fileToUpload.name);
}
</script>
</body>
</html>




Hopefully the above samples show how easy it can be to upload a file to the BI Platform as well as what is happening behind the scenes so you know what to look for when troubleshooting a request using a multipart request over HTTP.

6 Comments