BSP Programming: Handling Of Non-HTML Documents
BSP pages contain, effectively, HTML. When binary objects are requested, they are placed in the MIME repository and referenced from there. However, it is often necessary to handle binary objects/documents during the runtime of the program. It is not feasible to place these runtime documents in the MIME repository. (For any change in the MIME repository, transport records are written. This is usually not possible on a productive system, and is relatively slow compared to the runtime requirements of the running BSP application.)
Typical (real!) examples that we have seen:
-
For a personnel system, all colleagues’ pictures are available in a database table and must be displayed as part of an HTML page.
-
To use an ActiveX object (examples are Flash or SVG plug-in), it dynamically generates XML data that is available for the plug-in (via HTTP).
-
Some internal data is converted into a PDF document that must be displayed in the browser.
It would be interesting to look at three different ways to use/do this:
0.1.
0.2. Assume we are somewhere on a BSP page and wish to display the complete document. Typical example: a button is pressed for a receipt, and then a PDF document must be displayed.
0.3.
0.4. Next, a slightly more complex example is to display the new document as part of an HTML page. This requires that the HTML page must be rendered back and then, on a second HTTP request, the document is fetched and displayed in the same page.
0.5.
0.6. The last approach is to open a new window and display the document in the window.
0.7.
We’ve taken on a large challenge today, so let’s get cracking!
Test Harness
Our first step is to build a small test program to have a document available to display. As we don’t feel like generating PDF documents on the fly or reading images from some database table, we’ll just upload the test document. In all cases, we assume that either an image (.jpg, gif or .png) or some “known” document (.pdf, .doc, .xls, etc.) is specified.
After the document is uploaded, we have it “in our hands” and must do something with it. Keep in mind that after the response is processed, our session will be closed (stateless program).
First, we create a new BSP application and add a few page attributes:
<!code> file_length TYPE STRING
<!code> file_mime_type TYPE STRING
<!code> file_name TYPE STRING
<!code> file_content TYPE XSTRING
<!code> display_type TYPE STRING
<!code> display_url TYPE STRING
The four file_* attributes reflect the dynamic document that we “created” via an upload. Note that the content is of type XSTRING because we are working with binary documents.
The next step is to write the BSP application that will do the upload. After many hours:
<!code> Layout:
<!code>
<!code> <%@page language=”abap” %>
<!code> <%@extension name=”htmlb” prefix=”htmlb” %>
<!code>
<!code> <htmlb:content design=”design2002″ >
<!code> <htmlb:page>
<!code> <htmlb:form method = “post”
<!code> encodingType = “multipart/form-data” >
<!code>
<!code> <htmlb:radioButtonGroup id=”display_type” >
<!code> <htmlb:radioButton id = “inline” text=”Display Inline” />
<!code> <htmlb:radioButton id = “html” text=”Display Inside HTML Page” />
<!code> <htmlb:radioButton id = “window” text=”Display In New Window” />
<!code> </htmlb:radioButtonGroup>
<!code>
<!code> <htmlb:fileUpload id = “myUpload”
<!code> onUpload = “HandleUpload”
<!code> upload_text = “Display”
<!code> size = “90” />
<!code>
<!code> –
<!code>
<br>Name = <%= file_name%>
<!code>
<br>MIME-Type = <%= file_mime_type%>
<!code>
<br>Length = <%= file_length%>
<!code>
<!code> </htmlb:form>
<!code> </htmlb:page>
<!code>
<!code> OnInputProcessing:
<!code>
<!code> DATA: fileUpload TYPE REF TO CL_HTMLB_FILEUPLOAD.
<!code> fileUpload ?= CL_HTMLB_MANAGER=>GET_DATA(
<!code> request = request
<!code> id = ‘myUpload’
<!code> name = ‘fileUpload’ ).
<!code>
<!code> file_name = fileUpload->file_name.
<!code> file_mime_type = fileUpload->file_content_type.
<!code> file_length = fileUpload->file_length.
<!code> file_content = fileUpload->file_content.
<!code>
<!code> DATA: radioButtonGroup TYPE REF TO CL_HTMLB_RADIOBUTTONGROUP.
<!code> radioButtonGroup ?= CL_HTMLB_MANAGER=>GET_DATA(
<!code> request = request
<!code> id = ‘display_type’
<!code> name = ‘radioButtonGroup’ ).
<!code>
<!code> display_type = radioButtonGroup->selection.
The final results in the browser are:
Display Document Inline
With this simplest approach, we have an incoming HTTP request for an HTML page. Instead of processing the BSP page (thus rendering out HTML code), we just send out the dynamic document that we uploaded, so that it is displayed inline.
What do we have to do? Effectively, write the content that we already have available into the HTTP response and set the correct content data. Lastly, inform the BSP runtime that the response has been completely written, and that no further processing is required.
<!code> OnInputProcessing:
<!code>
<!code> … code previously displayed above …
<!code>
<!code> IF display_type = ‘inline’ AND XSTRLEN( file_content ) > 0.
<!code> DATA: response TYPE REF TO if_http_response.
<!code> response = runtime->server->response.
<!code> response->set_data( file_content ).
<!code> response->set_header_field( name = if_http_header_fields=>content_type
<!code> value = file_mime_type ).
<!code> ” response->set_header_field( name = if_http_header_fields=>content_length
<!code> ” value = file_length ).
<!code> response->delete_header_field( name = if_http_header_fields=>cache_control ).
<!code> response->delete_header_field( name = if_http_header_fields=>expires ).
<!code> response->delete_header_field( name = if_http_header_fields=>pragma ).
<!code> navigation->response_complete( ). ” signal that response is complete
<!code> RETURN.
<!code> ENDIF.
The IF statement checks that it is the inline test and that we actually have content available to display. The set_data() method writes the complete XSTRING into the response. The “Content-Type” HTTP header field is set. This MIME type is critical so the browser knows what is coming down the pipe.
Theoretically we also want to set the “Content-Length” HTTP header field. However, we found a small bug in the kernel :(. If we set the content length, and the response is also gzip’d when it is streamed to the browser, the content length is not reset to the shorter length. Therefore the browser waits forever for more data that is not coming. So, we use a trick. We don’t set the content length, and just let ICM set it after the gzip compression. (From our ICM expert: One additional remark I forgot to mention: it is good programming practice within our HTTP framework to <b>never</b> manually set the content length field. It is the kernel serialize code’s task to determine the actual serialized length. This may depend on various side effects like compression, chunking, etc.)
One can also consider deleting the three HTTP headers “Cache-Control”, “Expires” and “Pragma”. As BSP pages are effectively HTML pages with business data, the BSP runtime already pre-set these HTTP headers to indicate that BSP pages must not be cached. However, we are now reusing the HTTP response for our binary document. We can delete these headers.
The last problem is that after the onInputProcessing method, the layout is also processed. This results in all output from the layout also being written into the response. Thus the response_complete() call, which informs the BSP runtime that the response is completed and no further processing is required.
After the BSP application runs, we select a nice photo and see the picture displayed directly inside the browser window.
Display Document Inside HTML Page
Displaying the document inside the HTML page is slightly more complex. The problem is that in the response we must write HTML coding for the browser to render the page. The HTML coding must reference the dynamic document that we have available at this very moment. So where should we park the dynamic document until the browser has time to fetch it? (Keep in mind that once the response has been processed, the session will be closed and we will lose everything we had on hand.)
The solution is actually very simple and elegant. The ICM supports an excellent HTTP cache. Whenever a MIME object is retrieved from Web AS, it’s also added into the ICM cache. All other requests for the same document are served directly from the cache and do not require a switch to ABAP. These requests are processed in the kernel.
When we have the dynamic document on hand, we can just as well write it into the ICM cache. Thus, any HTTP requests for the document (actually for this specific URL!) will retrieve the document from the cache.
So the first interesting part of the code is to write the dynamic document directly into the ICM cache. The complete coding is:
<!code> OnInputProcessing:
<!code>
<!code> … code previously displayed above …
<!code>
<!code> IF display_type = ‘html’ AND XSTRLEN( file_content ) > 0.
<!code>
<!code> DATA: cached_response TYPE REF TO if_http_response.
<!code> CREATE OBJECT cached_response TYPE CL_HTTP_RESPONSE EXPORTING add_c_msg = 1.
<!code>
<!code> cached_response->set_data( file_content ).
<!code> cached_response->set_header_field( name = if_http_header_fields=>content_type
<!code> value = file_mime_type ).
<!code> cached_response->set_status( code = 200 reason = ‘OK’ ).
<!code> cached_response->server_cache_expire_rel( expires_rel = 180 ).
<!code>
<!code> DATA: guid TYPE guid_32.
<!code> CALL FUNCTION ‘GUID_CREATE’ IMPORTING ev_guid_32 = guid.
<!code> CONCATENATE runtime->application_url ‘/’ guid INTO display_url.
<!code>
<!code> cl_http_server=>server_cache_upload( url = display_url
<!code> response = cached_response ).
<!code> RETURN.
<!code>
<!code> ENDIF.
To write the information into the ICM cache, it’s necessary to create a complete HTTP response. Keep in mind that the browser will later send an HTTP request for this document, to which the ICM cache will return the cached HTTP response directly.
First, we create a new HTTP response object and add a new message. (The message is the actual buffers required to move the document from the ABAP VM into the kernel.)
The next few lines were already discussed above. We set the content into the response and the content type. The set_status() call is required to indicate to the browser that for this request-response cycle everything went perfectly.
The next aspect is to set the time that the dynamic document will stay in the ICM cache. Keep in mind that this time should be long enough for the browser to load all URLs referenced in the HTML page. However, there is no need to leave the document in the ICM cache for too long. Here a value of 3 minutes (180 seconds) is used. Anything between 1 and 5 minutes should be OK.
The more difficult problem is the URL to use. This URL is effectively the “address” of the dynamic document on the server. The browser will later fetch the document from the server with this key.
The first idea was to use the uploaded filename as part of the URL. In this case , take care to replace the ‘:’ and ‘/’ characters in the URL to make it a new valid URL. However, such a static type of URL does not scale very well. What happens if different people are running the same application, and uploading the same generic document (example: “travel_expenses.xls”)? Then each new response will overwrite the previous copy in the cache. Therefore, the recommendation is to use some form of random number (GUID)in the URL that is generated.
We could place the generated URL anywhere into the “namespace” of valid URLs. However, we recommend placing the URL into the “namespace” of the current active BSP application. Another nice touch you could consider is to also copy the document extension from the uploaded filename over into the URL. However, this is not critical. It’s more important that the MIME type is set correctly in the HTTP response, which we already do.
The last step is to place the document into the ICM cache.
With the above coding, we successfully created a new HTTP response in the ICM cache that can be addressed under the URL stored in “display_url” (page attribute of type STRING). The last step is to change the rendered HTML coding to also display the uploaded document. For this we just use an . The following HTML sequence is added in the layout, just before the end of the page.
<!code> Layout:
<!code>
<!code> … code previously displayed above …
<!code>
<!code> <% IF display_type = ‘html’ AND display_url IS NOT INITIAL. %>
<!code>
<!code> </iframe>
<!code> <% ENDIF. %>
<!code>
<!code> </htmlb:page>
<!code>
This is just an IF-guard to check for the specific case of displaying the document inline, plus the sequence to load the newly created URL.
The output is as expected. Both the data about the dynamic document and the document itself are displayed.
With this approach there are just two smaller problems that should not be forgotten. By default, the ICM cache is always configured. What happens if it is not available? You should really highlight this in the product documentation. The second problem is if the ICM cache is too small or flushed by someone. In that case we will get the dreaded !./BSP-ProgrammingHandling-Of-Non-HTML-Documents_004.jpg|width=13 height=16 /|src=./BSP-ProgrammingHandling-Of-Non-HTML-Documents_004.jpg! in the browser! But this is a small probability versus the pure win of this technique!</p>
Display Document In New Window
By now most of the difficult work is complete. For the final leg of our explorations, we would like to place the dynamic document into a new window. The biggest problem is just how to trigger opening a new window.
It is not possible to open a new browser window from the server. The simplest technique is to open the new window directly in the browser with a small JavaScript sequence: “window.open(url)”.
The first step is to require the dynamic document stored as a URL on the server. All this coding is already in place. We just change the IF-guard to include this new case.
<!code> OnInputProcessing:
<!code>
<!code> … code previously displayed above …
<!code>
<!code> IF ( display_type = ‘html’ OR display_type = “window” ) AND
<!code> XSTRLEN( file_content ) > 0.
<!code>
<!code> … code as previously displayed …
<!code>
<!code> ENDIF.
Next, add the code in the layout to open the new window. This is quickly done with:
<!code> Layout:
<!code>
<!code> … code previously displayed above …
<!code>
<!code> <% IF display_type = ‘window’ AND display_url IS NOT INITIAL. %>
<!code>
<!code> window.open(“<%=display_url%>”).focus();
<!code> </script>
<!code> <% ENDIF. %>
<!code>
<!code> </htmlb:page>
<!code>
With the final results:
!https://weblogs.sdn.sap.com/weblogs/images/13/BSP-ProgrammingHandling-Of-Non-HTML-Documents_005.JPG|height=359|width=604|src=https://weblogs.sdn.sap.com/weblogs/images/13/BSP-ProgrammingHandling-Of-Non-HTML-Documents_005.JPG|border=0!</body>
nice summary of file handling in BSP. However, please be aware that content in the serverside Mime Cache is no longer under access control (means both ICM authentication and ABAP authorization). So if you write a self-service scenario and place, say, your current payroll in PDF format in the mimecache, and ideally under the user's ID as the mime URL, then be aware that everybody else can read it also.
Please be careful with this technique, for secure documents pay the price and render the file/mime each time in ABAP again, without placing it in the cache.
Regards, Dieter Krisch
The weblog is good collection of information for uploading document. I am looking for the information how we can save the contents in the file system while upload and can give the link to the files later.
Regards
Ajay
Thsi document is very powerfull and helpfull . I just want to add a tips and tricks for increase the speed of displaying document.
You can store your generated document by SAP system in the file system , then you have to set the parameter icm/HTTP/file_access_x. For example for PDF file you can set the icm_HTTP/file_access as bellow :
PREFIX = /PDF, DOCROOT=/"directory in the file system", then in you BSP application you have just to make a link to the document as below :
http://localhost:port/PDF/document_name then the system will substitue "localhost:port/PDF" by your file directory .....
the concatenate issue is a serious problem in ABAP there is no way to maintain the trailing blanks.
I tried as suggested by you "the character" mode, but it doesn't work. It ignores trailing spaces.
SAP's Help says that the "Character mode" is the default behaviour for the concatenate, so theoretically it shouldn't have changed the result for your problem.
In this report:
REPORT ZTEST1 .
data: string1(10).
data: string2(10).
data: string3 type string.
string1 = 'test1 '.
string2 = 'test2 '.
concatenate string1 string2 into string3 in character mode.
write string3.
output is "test1test2".
Where am I wrong?
There IS a way, although awkward, to DO concatenate strings together without losing spaces:
You can start from the END.
SNIP
________
* get the table size
describe table result_buffer lines line_count.
* process the table starting from the END
while line_count > 0.
* get the next chunk
read table result_buffer index line_count
into wa_result_buffer.
* shift the result string to the right
* and insert the current chunk before the
* previous one
shift result right by buffer_size places.
replace section offset 0
length buffer_size of result
with wa_result_buffer.
line_count = line_count - 1.
endwhile.
_________
SNAP.
This procedure could be optimized in a way, so that you first shift the last line VERY much to the right (enough to never shift again) and then just past in all the other lines of the table without ever shifting again.
Christian
I would like to know how to save the file into SAP Transaction thro BSP. I have one requirement,For example,the captured file from the web should be attached into the Equipment master (Transaction Code IE02).Its possible via SAP GUI.But i would like to know how it can be done from BSP.The same example applicable to sales order also..
Also i wondering where these files get stored.
Anybody could help me out of this issue please?...
Raja T..
Need help regarding the following questions mentioned below.
1)We want to show some details in an iView pertaining to the person who has logged into the portal. We are calling a function module (when a user clicks on a workset in the portal) and passing some parameters to it. The function module pulls the data from the tables and generates a sap script which displays the data in a SAP Script Form.
Now our problem is how do we display the form in the iView?
2) We want to show some details in an iView pertaining to the person who has logged into the portal. We are calling a function module (when a user clicks on a workset in the portal) and passing some parameters to it. The function module pulls the data from the tables and converts the data into a pdf file.
How do we display the data in the .pdf format in the iView.
Thanks in advance.
Jayesh
I didn't give you this link to post your same question.
This weblog from Brian shows exactly how display documents that are not standard HTML. For an iView you just have to create a Portal Enabled BSP that does that. I would suggest if you are having problems to post questions in either the BSP Forum or one of the Portal Forums.
The BSP forum is the correct place to discuss BSP. However, keep in mind that SDN is to help and to teach; to distribute knowledge. It is (mostly) not the place to pick up canned answer. You will unfortunately have to do the work yourself. We can only guide you (a little).
Given that you SAP form, just use a function module to convert this into a PDF document. I don't know the complete detail, do know that this has been discussed a number of times in the forum. Why not search through it with some hitswords.
As to your question on how to display a PDF in an iView, exactly as described in this weblog. Just make it work first without the portal. It should then work (theorectically) immediately in an iView as well.
If you have problems displaying a PDF document, you can reduce your code to about 25 lines, and we can review it. But the 25 lines you will have to bring to the table.
brian
Nice Weblog,Actually I am facing a peciular problem. I am simply using the following tag to open the pdf in new window.
User manual
And user manual is already uploaded in the MIME objects in docs/user_manual.pdf.
Actually problem is PDF contents are not shown on the first click of the link.
But displayed properly on the second click of the link.
Please provide me some solution for this why this is happening.
Regards
Aashish Garg
> Please provide me some solution for this
> why this is happening.
That is the nice thing I learned about live. It is much better for me to provide a sympathetic ear, listen to your problem, and later share in your joy when you solve the problem. To put it mildly, I have no answer. All I can do, is to recommend a way for you to continue your quest.
Two things: look at whether you are using HTTPS. There are a number of issues with HTTPS and PDF. Same for GZIP and PDF. For some reading, let your eyes rove over the wiki in SDN. For example:
https://wiki.sdn.sap.com/wiki/display/BSP/Handling+Binary+Data
https://wiki.sdn.sap.com/wiki/display/BSP/PDF
https://wiki.sdn.sap.com/wiki/display/BSP/GZIP
The second aspect, is that you need to know what is really happening when you click that link the first time. Is a request really send to the server, does the server really answer a PDF, etc. For this, install any HTTP and just look at what is actually happening. Just can start surfing from the link below, and then look at the right hand column at tools. Both HttpWatch and Fiddler can be used.
https://wiki.sdn.sap.com/wiki/display/BSP
And once you have tamed the beast, do not forget to share your experience in SDN (recommended is the BSP forum AND to update anyone of these wiki pages if you learned something new). You can be sure we are going to enjoy reading about your success.
regards, brian
I was just wondering if you could also display outlook messages within your http response? If yes then what would be possible ways of doing that? I have had a go but am not quite sure what content type needs to be mapped for the response?
I used your solution to display an Interactive Adobe form, I know there is another way of using an Interactive Adobe form in BSP but the requirement is to diaplay an Interactive Adobe form in BSP with some buttons on the top, this functionality would be very easy in Web Dynpro ABAP using UI elements, however I am asked to develop this in BSP.
I have been trying to research if there is way to extract the displayed HTML document as XSTRING in BSP. We use the below code to upload a document .....
cached_response->set_data( file_content ).
cached_response->set_header_field( name = if_http_header_fields=>content_type
value = file_mime_type ).
cached_response->set_status( code = 200 reason = 'OK' ).
cached_response->server_cache_expire_rel( expires_rel = 180 ).
DATA: guid TYPE guid_32.
CALL FUNCTION 'GUID_CREATE'
IMPORTING
ev_guid_32 = guid.
CONCATENATE runtime->application_url '/' guid INTO display_url.
cl_http_server=>server_cache_upload( url = display_url
response = cached_response ).
RETURN.
Is there a way to extract an HTML document as XSTRING? I am trying to capture the data in BSP entered in the Online Adobe Form by the User.
Regards,
Shishir.P