Recently, we faced a scenario where we had to retrieve the documents attached to a Business Object and then had to display the attachment in the NetWeaver Portal.
I faced some problems in achieving this and found that many other
fellow SDN'ers
faced similar issues( like document getting corrupted while displaying ), so i decided to jot down this blog discussing how to fetch the attached documents of objects when we store them in an application transaction usingServices for the Object
or useSAPoffice
as the document storage location and then how to display the document in aBusiness Server Page(BSP)
and in aWebDynpro Java application.
There are general functions that we want to execute, like creating a note for an object, sending an object, or starting a workflow for an object, some of which can only be realized for a specific object type with a great deal of programming.
If we store documents in an application transaction using "Services for the Object" or use "SAPoffice" as the document storage location, the attachments are linked with the application objects via binary links of the
Object Relationship Service (ORS)
.CL_BINARY_RELATION=>READ_LINKS_OF_OBJECTS
. After calling this, the links appear in the internal table ET_LINKS and we get the objects of type MESSAGE that are given an ID .
I created a remote enabled “Z” Function module encapsulating the call to the method of the class CL_BINARY_RELATION, as follows:
h5. IMPORT
Parameter name | Type Specification | Associated Type |
---|---|---|
I_INSTANCEID | TYPE | SIBFLPORB-INSTID |
I_TYPEID | TYPE | TYPEID |
I_CATID | TYPE | CATID |
h5. EXPORT
Parameter name | Type Specification | Associated Type |
---|---|---|
DOCUMENT_ID | TYPE | SOFOLENTI1-DOC_ID |
EXCEPTION_STRING | TYPE | STRING |
h5. IMPLEMENTATION
DATA: is_object TYPE sibflporb,
et_links TYPE obl_t_link,
et_links_s TYPE obl_s_link ,
RESULT TYPE C,
icx_obl_parameter_error TYPE REF TO cx_obl_parameter_error ,
icx_obl_internal_error TYPE REF TO cx_obl_internal_error,
icx_obl_model_error TYPE REF TO cx_obl_model_error,
*************initialize*******
is_object-instid = I_INSTANCEID. * ( Instance ID )
is_object-typeid = I_TYPEID. * (Type of Objects in Persistent Object References EG: BUS2045 FOR Business Object INSPECTION LOTS)
is_object-catid = I_CATID. *(Category of Objects in Persistent Object References default BO)
***********************read attachments of relationship type 'ATTA'
TRY.
CALL METHOD cl_binary_relation=>read_links_of_binrel
EXPORTING
is_object = is_object
IP_LOGSYS =
ip_relation = 'ATTA'
IP_ROLE =
IP_PROPNAM =
IP_NO_BUFFER = SPACE
IMPORTING
et_links = et_links
ET_ROLES =
.
CATCH cx_obl_parameter_error INTO icx_obl_parameter_error.
EXCEPTION_STRING = icx_obl_parameter_error->get_longtext( ).
CATCH cx_obl_internal_error INTO icx_obl_internal_error .
EXCEPTION_STRING = icx_obl_internal_error->get_longtext( ).
CATCH cx_obl_model_error INTO icx_obl_model_error.
EXCEPTION_STRING = icx_obl_model_error->get_longtext( ).
ENDTRY.
IF et_links[] IS NOT INITIAL.
SORT et_links BY utctime DESCENDING .
READ TABLE et_links INDEX 1 INTO et_links_s .
document_id = et_links_s-instid_b .
**We can pass the entire list of attachments as export parameter or sort by time and send only the latest attachment. **
ENDIF.
CALL FUNCTION 'SO_DOCUMENT_READ_API1'
EXPORTING
document_id = docid
FILTER = 'X '
IMPORTING
DOCUMENT_DATA = DOCUMENT_DATA
TABLES
object_header = object_header
object_content = object_content
OBJECT_PARA =
OBJECT_PARB =
ATTACHMENT_LIST =
RECEIVER_LIST =
CONTENTS_HEX = CONTENTS_HEX
EXCEPTIONS
DOCUMENT_ID_NOT_EXIST = 1
OPERATION_NO_AUTHORIZATION = 2
X_ERROR = 3
OTHERS = 4
.
DATA:object_header_str TYPE solisti1,
object_content_str TYPE solisti1,
FILE_TYP TYPE STRING.
CLEAR:temp,object_header_str,object_content_str .
IF object_header IS NOT INITIAL.
LOOP AT object_header INTO object_header_str .
IF sy-tabix EQ '1'.
SPLIT object_header_str-line AT '.' INTO file_name FILE_TYP.
ENDIF.
IF sy-tabix EQ '2'.
SPLIT object_header_str-line AT '=' INTO temp file_format.
ENDIF.
ENDLOOP.
ENDIF.
DATA:file_content_temp TYPE string.
data:input_length type i,
lin type i.
CLEAR file_content_temp .
if file_format EQ 'BIN'.
************concatenate content as xstring*********
IF CONTENTS_HEX IS NOT INITIAL.
DESCRIBE TABLE CONTENTS_HEX LINES lin.
input_length = lin * sy-tleng .
CALL FUNCTION 'SCMS_BINARY_TO_XSTRING'
EXPORTING
input_length = input_length
FIRST_LINE = 0
LAST_LINE = 0
IMPORTING
BUFFER = FILE_CONTENT
tables
binary_tab = CONTENTS_HEX
EXCEPTIONS
FAILED = 1
OTHERS = 2
.
IF sy-subrc <> 0.
MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ENDIF.
if FILE_TYP EQ 'pdf'.
file_mime_type = 'application/pdf' .
else if FILE_TYP EQ 'doc'.
file_mime_type = 'application/msword' ..
else if FILE_TYP EQ 'xls'.
file_mime_type = 'application/vnd.ms-excel' .
*similarly for others
ENDIF.
ELSE.
file_mime_type = 'text/html' .
IF object_content IS NOT INITIAL.
CALL FUNCTION 'SCMS_TEXT_TO_XSTRING'
EXPORTING
FIRST_LINE = 0
LAST_LINE = 0
MIMETYPE = ' '
IMPORTING
BUFFER = FILE_CONTENT
TABLES
text_tab = object_content
EXCEPTIONS
FAILED = 1
OTHERS = 2
.
IF sy-subrc <> 0.
MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ENDIF.
ENDIF.
endif.
*************display in Browser***********************
IF XSTRLEN( file_content ) > 0.
DATA: cached_response TYPE REF TO if_http_response.
CREATE OBJECT cached_response TYPE cl_http_response EXPORTING
add_c_msg = 1.
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.
ENDIF.
Now, we would create a similar application in
WebDynpro-Java
.So, create a WebDynpro project in the NetWeaver Developer Studio. Create an RFC adaptive model for the required Function Modules,( Z Function module to get the latest attachment and the standard function module SO_DOCUMENT_READ_API1).
We would display the attachment in a popup window, so create the window
“PopupWindow”
and the view“AttachmentView”
apart from the main window“ReadAttachmentWindow”
and main View“ReadAttachmentComponentView”
.
The main view contains an
input field
and abutton
while the popup view contains aniframe
and abutton
to close the popup.The project structure would be as follows:
Map the controller context to the model as
Edit and map the View Context for the “ReadAttachmentComponentView” view to the Component Controller context as
Map the popup view context to the component controller context as
Now we create some methods in our component controller, which will be responsible for executing the models, processing the values, setting context values etc.
public void getDocID( )
public void openPopUp( )
public void closePopUp( )
public byte[] createAttaSource( )
BIN
when we need to read rows of bytes from tableContents_Hex
and create a single byte stream to re-create the attachment.public java.lang.String createTxtSrc( )
ASC
when we have to read "lines" of strings from tableObject_Content
and create a single string.public void prepToOpenAtta( )
public void getAttaDoc( )
In “ReadAttachmentComponentView”,the method corresponding to the button action “
getDocument
”
public void onActiongetDocument(com.sap.tc.webdynpro.progmodel.api.IWDCustomEvent wdEvent )
When we enter the Instance Id of the business object(for eg:Inspection Lot Number) and press the button in the main view (“ReadAttachmentComponentView”), the attached document is retrieved , processed , a web resource is generated and the URL is retrieved and set for iFrame in the popup view.
Hope this blog would help SDN'ers facing problems in retrieving and displaying attachments.
Note: The code provided is for illustration purposes only and so it does not gurantee completeness nor correctness!!