Integration Blog Posts
cancel
Showing results for 
Search instead for 
Did you mean: 
SiddarudhKeroor
Explorer
1,594

In many SAP Integration Suite (CPI) scenarios, we receive Excel files from SFTP or other channels and need to process them further — often by converting them into CSV. However, SAP CPI does not support Apache POI out of the box, and adding external libraries is not always feasible.

so, there's a way out, writing my first blog hope i didn't make much mistakes.

🎯 Convert XLSX file to CSV in SAP CPI Using Groovy (No External Libraries)

In this blog, I’ll show how to read an XLSX file, extract a specific worksheet by name, and convert it into CSV using pure Groovy, leveraging the fact that XLSX files are ZIP archives.

🧩Key Highlights of This Approach

  •  No Apache POI or external libraries

  • Works with payloads as (byte[], InputStream, or String)

  • Sheet selection by sheet name.

🛠️How the Solution Works (High Level)

  1. Read the incoming payload safely as byte[]

  2. Treat the XLSX file as a ZIP archive

  3. Extract:

    • workbook.xml → map sheet name to sheet ID

    • sharedStrings.xml → resolve string values

    • sheetX.xml → actual worksheet data

  4. Identify the correct worksheet by sheet name

  5. Convert rows and cells into CSV format

  6. Set CSV as the message body

🧪Groovy Script

import com.sap.gateway.ip.core.customdev.util.Message
import java.util.zip.ZipInputStream
import java.io.*

def Message processData(Message message) {

    // =====================================================
    // CONFIG – CHANGE SHEET NAME HERE ONLY
    // =====================================================
    final String TARGET_SHEET_NAME = "Sales- Input"   // <-- change if needed

    // --- 1. Safely read payload as byte[] (SFTP safe) ---
    def body = message.getBody()
    byte[] xlsx

    if (body instanceof byte[]) {
        xlsx = body
    } else if (body instanceof InputStream) {
        xlsx = body.bytes
    } else if (body instanceof String) {
        xlsx = body.getBytes("UTF-8")
    } else {
        throw new Exception("Unsupported payload type: ${body?.getClass()}")
    }

    if (!xlsx || xlsx.length == 0) {
        throw new Exception("Excel file is empty")
    }

    // --- 2. Open XLSX (ZIP) ---
    def sharedStrings = []
    Map<String, String> sheetNameToId = [:]
    Map<String, byte[]> sheetFiles = [:]

    InputStream sheetStream = null

    def zip = new ZipInputStream(new ByteArrayInputStream(xlsx))
    def entry

    while ((entry = zip.nextEntry) != null) {

        def baos = new ByteArrayOutputStream()
        byte[] buffer = new byte[4096]
        int len
        while ((len = zip.read(buffer)) > 0) {
            baos.write(buffer, 0, len)
        }

        def contentBytes = baos.toByteArray()

        // ---- workbook.xml (sheet name → rId) ----
        if (entry.name == "xl/workbook.xml") {
            def wb = new XmlSlurper().parse(new ByteArrayInputStream(contentBytes))
            wb.sheets.sheet.each {
                sheetNameToId[it.@name.text()] = it.@'r:id'.text()
            }
        }

        // ---- sharedStrings.xml ----
        else if (entry.name == "xl/sharedStrings.xml") {
            def xml = new XmlSlurper().parse(new ByteArrayInputStream(contentBytes))
            xml.si.each { si ->
                if (si.t) {
                    sharedStrings << si.t.text()
                } else {
                    sharedStrings << si.r.collect { it.t.text() }.join('')
                }
            }
        }

        // ---- worksheets ----
        else if (entry.name.startsWith("xl/worksheets/sheet")) {
            def sheetFile = entry.name.replace("xl/worksheets/", "")
            sheetFiles[sheetFile] = contentBytes
        }
    }
    zip.close()

    // --- Resolve correct worksheet ---
    def relId = sheetNameToId[TARGET_SHEET_NAME]
    if (!relId) {
        throw new Exception("Sheet '${TARGET_SHEET_NAME}' not found")
    }

    int sheetIndex = relId.replace("rId", "").toInteger()
    def sheetFileName = "sheet${sheetIndex}.xml"

    if (!sheetFiles[sheetFileName]) {
        throw new Exception("Worksheet file ${sheetFileName} not found")
    }

    sheetStream = new ByteArrayInputStream(sheetFiles[sheetFileName])

    // --- 3. Convert sheet to CSV (UNCHANGED) ---
    def sheetXml = new XmlSlurper().parse(sheetStream)
    def csv = new StringBuilder()

    sheetXml.sheetData.row.each { row ->
        def line = row.c.collect { cell ->
            def raw = cell.v?.text() ?: ''
            def value = (cell.@t == 's' && raw) ? sharedStrings[raw.toInteger()] : raw

            if (value.contains(',') || value.contains('"') || value.contains('\n')) {
                "\"${value.replace('"', '""')}\""
            } else {
                value
            }
        }.join(',')

        csv.append(line).append('\n')
    }

    // --- 4. Output ---
    message.setBody(csv.toString())
    message.setHeader("Content-Type", "text/csv")
    return message
}

Note *Make sure to change the sheet name accordingly (not excel file name).

🚧Adapter Compatibility

This Groovy script is independent of the sender adapter and works with SFTP, HTTP, and AS2, as long as the incoming payload is the raw XLSX file (binary or InputStream).

For HTTP senders, ensure the Excel file is not Base64-encoded and not wrapped inside multipart form-data with additional fields.

⚠️Limitations & Notes

  • This solution handles:

    • Strings
    • Numeric values
  • It does not handle:
    • Formulas
    • Styles
    • Dates with formatting
    • Large Excel files may impact performance (DOM parsing)

Conclusion

This Groovy-based solution provides a simple and dependency-free way to convert Excel files into CSV inside SAP CPI.

If you are dealing with Excel files from SFTP and need quick processing without external libraries, this approach can save a lot of effort.

💡 What’s Next?
This solution handles direct XLSX payloads efficiently.
With small enhancements, the same approach can be extended to support Base64 payloads from HTTP senders, multipart uploads, and runtime-configurable sheet names.

hope it helped you...🙂

1 Comment