Skip to Content


Uploading/Downloading Files into/from SAP


Recently, I was faced with the task of implementing a file upload and download via a JSP application into and from the SAP Records Management. “Shouldn’t be big deal” I thought by myself assuming that I only have to plug together some predefined functions and everything will already be done. But this didn’t work out. I was faced with this task during 2005 on the basis of the SAP NetWeaver Developer Studio Version 2.0.11, a J2EE Engine with a kernel version 6.40, a SAP Financials and a SAP Dispute/Records-Management application whereas the last two applications could either resist on one SAP system or on two SAP systems embedded into an ALE scenario (this maybe considered to be old fashioned but such constellations still often occur amongst many SAP customers). If you perform a file upload via the SAP standard as of SAP NetWeaver Application Server Release 6.40 SP11 for the ABAP stack and SP13 for the Java stack even virus scans will be performed automatically via the following functions:

    • Function Module GUI_UPLOAD in the SAP ABAP Stack
    • HTTP_UPLOAD using Business Server Pages
    • FileUpload of the Web Dynpro for JAVA

But if you do something “exotic” like the development of native JSPs on top of the SAP J2EE Engine there are no predefined and standardized libraries or pluggable components which provide a suitable set of functions for uploading or downloading files. Therefore, there are several steps that must be carried out when implementing a JSP application which cannot use any of the previously mentioned SAP standard functions.

At first the GUI must provide an upload dialog. This can be achieved by the following steps:

    1. Usage of encryption type mulitpart/form-data.
    2. Creation of input fields of type file.
    3. Insertion of buttons or hyperlinks for either uploading files or removing uploaded files.

On the server side suitable classes have to handle such a request appropriately. For that purpose there is no SAP standard library that provides functions like

    1. parsing multipart/form-data requests,
    2. scanning files for virus infections,
    3. checking the mime types of the uploaded files against a set of allowed mime types,
    4. limiting the file size to an upper limit or
    5. limiting the number of concurrent uploads.

Considering the virus scan SAP provides the VSI (virus scan interface) which may be used to implement a virus scan. In http://www.sdn.sap.com/irj/servlet/prt/portal/prtroot/docs/library/uuid/b00a87e5-0601-0010-87a7-9c0456cf8436 you can find an official coding sample how the VSI can be integrated into your application but it only treats the VirusInfectionException. The following code snippet shows in more detail how this may be done:



<!codepublic void parseRequest(HttpServletRequest request, HttpServletResponse
<!code>                          response)
<!code>      throws IOException {
<!code>      …
<!code>            /*prepare for the virus scan */
<!code>            byte[] byteArrayToBeScanned = new byte[filesize];
<!code>            byte[] tmpByteArray;
<!code>            offset = 0;
<!code>            for (int l = 0; l < binaryFileContent.size(); l++)
<!code>            {
<!code>                   tmpByteArray = (byte[])binaryFileContent.get(l);
<!code>                   for (int j = 0; j < tmpByteArray.length; j++) {
<!code>                          byteArrayToBeScanned[offset + j] =
<!code>                                 tmpByteArray[j];
<!code>                   }
<!code>                   offset+=tmpByteArray.length;
<!code>            }
<!code>            /* Now the actual virus scan can be performed */
<!code>            Context ctx;
<!code>            try {
<!code>                   ctx = new InitialContext();
<!code>                   VSIService vsiService = (VSIService)
<!code>                          ctx.lookup(VSIService.JNDI_NAME);
<!code>                   if (vsiService != null) {
<!code>                          /* get a scan instance */
<!code>                          Instance myInstance = null;
<!code>                          try {
<!code>                                  myInstance = vsiService.getInstance();
<!code>                                 if(myInstance == null){               
<!code>                                 /*Virus Scanner is not configured log
<!code>                          this => VSIServiceException */
<!code>                                 }
<!code>                          myInstance.scanBytes
<!code>                          (filename,byteArrayToBeScanned,
<!code>                                 byteArrayToBeScanned.length);
<!code>                   /* if a virus is found =>                                    
<!code>                   VirusInfectionException    */


<!code>                   /* VirusInfectionException:
<!code>                                    file has a virus, logging
<!code>                                    entry to be created, handle this
<!code>                                     exception as a user error, throw away
<!code>                                        files to be uploaded */
<!code>                                 binaryFileContent = null;
<!code>                                 byteArrayToBeScanned = null;
<!code>                                 filesize = 0;
<!code>                                 /* user message to be send to
<!code>                          the GUI: “error.fileIsInfected” */
<!code>                          } catch (VirusScanException vse) {
<!code>                                 /* scan service has been
<!code>                                    installed but can not cope
<!code>                                    with the file to be scanned
<!code>                                    the file will be thrown away
<!code>                                 logging entry to be created  */
<!code>                                 binaryFileContent = null;
<!code>                                 byteArrayToBeScanned = null;
<!code>                                 filesize = 0;
<!code>                                 /* inform user that that virus
<!code>                                 scanner cannot say without any
<!code>                                 doubt  whether there aren’t
<!code>                                 viruses inside the file
<!code>                                 “error.fileMightBeInfected”  */
<!code>                          } catch (VSIServiceException vse) {     
<!code>                             /*  VSIServiceException: the scan
<!code>                                 servicehasn’t been fully
<!code>                                installed no user message
<!code>                                 logging entry keep file
<!code>                                 Perhaps the virus scan has
<!code>                                 already be done by the firewall
<!code>                                 and therefore the scan service
<!code>                                 hasn’t been   installed
<!code>                                 “parseRequest”,
<!code>                          “logmsg.virusScanNotActive”,  */
<!code>                          } catch (Exception e) {
<!code>                                 /* catch all other Exceptions
<!code>                                  * expected error an log them
<!code>                                  * “error.uploadError”,
<!code>                                  */
<!code>                                 binaryFileContent = null;
<!code>                                 byteArrayToBeScanned = null;
<!code>                                 filesize = 0;
<!code>                          } finally {
<!code>                                 /* release the scan instance */
<!code>                                 vsiService.releaseInstance
<!code>                                                      (myInstance);
<!code>                          }
<!code>                   }
<!code>            } catch (NamingException e) {
<!code>                   binaryFileContent = null;
<!code>                   byteArrayToBeScanned = null;
<!code>                   filesize = 0;
<!code>                   /* “error.uploadError”, */
<!code>            }
<!code>           
<!code>  }
<!code>   

When the virus scan is implemented and the web application is deployed and running on the J2EE Server, the scanner has to be activated before it can perform virus scans. This means to use partner products to perform the actual virus scan and to configure the virus scan provider of the J2EE Engine, for instance by means of the Visual Administrator.

Having uploaded the files to the J2EE Engine they have to be sent into the SAP system. This can be done with the aid of the SAP Java Connector (JCO). For that purpose the files have to be converted from their binary format into the specific format of the JCO table which is a representation of a database table of the SAP system. The field for the file content of such a table could be of type RAW of the length 255. In this case length 255 has been chosen because this is the maximum length that is allowed for fields, which have to pass an ALE interface. Finally, to store the files in the SAP Records Management various functions are available. BAPI_SRM_DOC_CHECKIN_VIA_TAB is one of them. Since the field of the table for the file content is of length 1022 another conversion of the internal representation of the file has to be carried out, one additional source of errors. Since many BAPIs for checking in files do not perform an internal virus scan it becomes necessary to do this in advance as outlined previously.

Downloading a file utilizes the same techniques. A BAPI call via the JCO is triggered by clicking on a button or on a hyperlink on client side. The data is transferred from the SAP system into the memory of the J2EE engine and, assuming that the representation of such a file is of type RAW of length 1022 the browser specific download dialog could be triggered by the following servlet (jcotable denotes the corresponding table. Attachment is a utility class which stores the file content and some additional information. The file content and the additional information are attached to the http request as an attribute).



<!codepackage com.sap.example;
<!codeimport java.io.IOException;
<!codeimport java.io.OutputStream;
<!codeimport javax.servlet.ServletException;
<!codeimport javax.servlet.http.HttpServlet;
<!codeimport javax.servlet.http.HttpServletRequest;
<!codeimport javax.servlet.http.HttpServletResponse;
<!codeimport com.sap.mw.jco.JCO;
<!codeimport com.sap.example.Attachment;
<!codepublic class DisputeDisplayAttachment extends HttpServlet {
<!codepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
<!code>      int lastValidByteIndex = -1;
<!code>      int lastValidLine = -1;
<!code>      final String method = “doGet”;
<!code>      OutputStream out = response.getOutputStream();
<!code>      Attachment da = (Attachment)                request.getAttribute(Constants.ATTACHMENT);
<!code>      String mimeType = da.getMimeType();
<!code>      int documentSize = da.getDocumentSize();
<!code>      JCO.Table jcotable = da.getBinaryContent();
<!code>      String documentName = da.getDocumentName();
<!code>      int numberOfRows = jcotable.getNumRows();
<!code>      lastValidByteIndex = documentSize%1022;
<!code>      lastValidLine = numberOfRows-1;
<!code>      if (lastValidByteIndex == -1) {
<!code>            /error/
<!code>            return;
<!code>      }
<!code>      response.setContentType(mimeType);
<!code>      response.setContentLength( documentSize );
<!code>      response.setHeader(
<!code>            “Content-Disposition”,
<!code>            “attachment; filename=” + documentName);
<!code>      for (int i = 0; i < lastValidLine; i++) {
<!code>            jcotable.setRow(i);
<!code>            out.write(jcotable.getByteArray(“LINE”));
<!code>            documentSize-=1022;
<!code>      }
<!code>      jcotable.setRow(lastValidLine);
<!code>      out.write(jcotable.getByteArray(“LINE”), 0, documentSize);
<!code>      out.flush();
<!code>      out.close();
<!code>  }
<!codepublic void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
<!code>      doGet(request, response);
<!code>  }
<!code>  }

Thus, concluding our considerations we can state that there are many possibilities to implement the upload and download of files. But if you do not want to be constrained to the ABAP Stack, to BSPs or to Web Dynpro there are many requirements which are not met by a predefined set of functions albeit the upload and download of files is a quite elementary requirement. This brings us immediately to a more general question, namely whether future releases of SAP NetWeaver will provide a mature and fully developed platform. We will see.


                                                                  

To report this post you need to login first.

3 Comments

You must be Logged on to comment or reply to a post.

    1. RĂ¼diger Bachmann
      Hier are the original classes:
      (DisputeAttachment corresponds to the missing class)
      /*
      * Created on Nov 18, 2004
      *
      * To change the template for this generated file go to
      * Window>Preferences>Java>Code Generation>Code and Comments
      */
      package com.sap.fin.fscmbase;

      import java.io.IOException;
      import java.io.OutputStream;

      import javax.servlet.ServletException;

      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import com.sap.mw.jco.JCO;
      import com.sap.fin.ebpp.model.DisputeAttachment;
      import com.sap.fin.util.hinting.HinterLocation;

      /**
      * @author d023975
      *
      * To change the template for this generated type comment go to
      * Window>Preferences>Java>Code Generation>Code and Comments
      */
      public class DisputeDisplayAttachment extends HttpServlet {

           private static HinterLocation loc = HinterLocation.getLocation(“com.sap.fin.fscmbase.DisputeDisplayAttachment”);

           /**
            *  Description of the Method
            *
            *@param  request               Description of Parameter
            *@param  response              Description of Parameter
            *@exception  IOException       Description of Exception
            *@exception  ServletException  Description of Exception
            */
           public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
                int lastValidByteIndex = -1;
                int lastValidLine = -1;
                final String method = “doGet”;
                loc.entering(method);
                OutputStream out = response.getOutputStream();
                DisputeAttachment da = (DisputeAttachment) request.getAttribute(FscmConstants.DISPUTE_ATTACHMENT);
                String mimeType = da.getMimeType();
                int documentSize = da.getDocumentSize();
                JCO.Table jcotable = da.getBinaryContent();
                String documentName = da.getDocumentName();
                int numberOfRows = jcotable.getNumRows();
                lastValidByteIndex = documentSize%1022;
                
                lastValidLine = numberOfRows-1;
                
                if (loc.beDebug()) {
                     loc.debugT(method, “ByteIndex: ” + lastValidByteIndex + ” LineIndex: ” + lastValidLine);
                }
                if (lastValidByteIndex == -1) {
                     loc.errorT(method, “Download error. Could not find EOF in document ” + documentName);
                     loc.exiting();
                     return;
                }
                response.setContentType(mimeType);
                response.setContentLength( documentSize );
                response.setHeader(
                     “Content-Disposition”,
                     “attachment; filename=” + documentName);
                for (int i = 0; i<lastValidLine; i++) {
                     jcotable.setRow(i);
                     out.write(jcotable.getByteArray(“LINE”));
                     documentSize-=1022;
                }
                jcotable.setRow(lastValidLine);
                out.write(jcotable.getByteArray(“LINE”), 0, documentSize); // lastValidByteIndex + 1);
                out.flush();
                out.close();

                loc.exiting();
           }
           /**
            *  Description of the Method
            *
            *@param  request               Description of Parameter
            *@param  response              Description of Parameter
            *@exception  IOException       Description of Exception
            *@exception  ServletException  Description of Exception
            */
           public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
                doGet(request, response);
           }

      }

      /*
      * Created on Nov 12, 2004
      *
      * To change the template for this generated file go to
      * Window>Preferences>Java>Code Generation>Code and Comments
      */
      package com.sap.fin.ebpp.model;

      import com.sap.fin.ebpp.constants.R3AccessConstants;
      import com.sap.fin.ebpp.constants.SettingsConstants;
      import com.sap.fin.util.hinting.HinterLocation;
      import com.sap.fin.webbase.framework.ActionMessage;
      import com.sap.fin.webbase.framework.ActionMessages;
      import com.sap.fin.webbase.framework.R3Messages;

      import com.sap.mw.jco.JCO;
      import com.sap.mw.jco.JCO.Table;

      /**
      * @author d023975
      *
      * To change the template for this generated type comment go to
      * Window>Preferences>Java>Code Generation>Code and Comments
      */
      public class DisputeAttachment {

           private static HinterLocation loc = HinterLocation.getLocation(“com.sap.fin.ebpp.model.DisputeAttachment”);
           private String OBJECTID;
           private String IDCLASS;
           private String DESCRIPTION;
           private String documentName;
           private int documentSize;
           private String mimeType;
           private Table binaryContent;

           public DisputeAttachment() {
           }
           public DisputeAttachment(
                ActionMessages messages,
                BillerDirectUser user,
                CustomerData customer,
                String caseGuid,
                String docID) {
                this();
                final String meth = “DisputeAttachment”;
                loc.entering(
                     meth + “docID” + docID + ” caseGuid: ” + caseGuid + ” customer: ” + customer + ” messages: ” + messages);
                loc.debugT(meth, “Parameters: caseGuid {0} docID {1} “, new Object[] { caseGuid, docID });

                JCO.Table binaryContent = null;

                DisputeAttachmentsList attachmentsList = customer.getDisputes().getAttachmentsList(caseGuid);
                OBJECTID = docID;
                IDCLASS = attachmentsList.getIDCLASS(docID);
                DESCRIPTION = attachmentsList.getDESCRIPTION(docID);

                JCO.Function function = customer.modelMan.getClientFunction(R3AccessConstants.GET_ATTACHMENT_FROM_DISPUTE);
                if (function == null) {
                     // write a log entry with highest priority
                     loc.fatalT(
                          meth,
                          “function module”
                               + com.sap.fin.ebpp.constants.R3AccessConstants.GET_ATTACHMENT_FROM_DISPUTE
                               + ” not found in Backend.”);
                     messages.addMessage(
                          new ActionMessage(SettingsConstants.SYSMSGBUNDLE, ActionMessage.FATAL, “error.global.headline”));
                }

                JCO.Field objectid = function.getImportParameterList().getField(R3AccessConstants.OBJECTID);
                objectid.setValue(OBJECTID);
                JCO.Field idclass = function.getImportParameterList().getField(R3AccessConstants.IDCLASS);
                idclass.setValue(IDCLASS);

                customer.convertBusinessPartnerToJCO(
                     function.getImportParameterList().getStructure(R3AccessConstants.I_PARTNER));

                loc.debugT(
                     meth,
                     com.sap.fin.ebpp.constants.R3AccessConstants.GET_ATTACHMENT_FROM_DISPUTE + “: before calling backend”);

                customer.modelMan.executeFunctionNamed(function);

                messages.addR3Messages(
                     new R3Messages(
                          function.getTableParameterList().getTable(com.sap.fin.ebpp.constants.R3AccessConstants.T_MESSAGES)));
                loc.debugT(
                     meth,
                     com.sap.fin.ebpp.constants.R3AccessConstants.GET_ATTACHMENT_FROM_DISPUTE + “: after calling backend”);

                binaryContent = function.getTableParameterList().getTable(R3AccessConstants.BIN_CONTENT);
                if (!messages.hasErrorsOrWorse() & binaryContent != null) {
                     fillDocumentContent(binaryContent);
                }

                documentName = (String) function.getExportParameterList().getField(R3AccessConstants.COMP_ID).getValue();
                mimeType = (String) function.getExportParameterList().getField(R3AccessConstants.MIMETYPE).getValue();
                documentSize = ((Integer)function.getExportParameterList().getField(R3AccessConstants.COMP_SIZE).getValue()).intValue();
                
                loc.exiting();

           }

           private void fillDocumentContent(Table binaryContent) {
                
                this.binaryContent = binaryContent;

           }
           public Table getBinaryContent() {
                
                     return binaryContent;

           }

           public int getDocumentSize(){ return documentSize;
           }
           public String getMimeType() { return mimeType;
           }
           public String getDocumentName() {return documentName;
           }

      }

      (0) 

Leave a Reply