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.
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.
No Apache POI or external libraries
Works with payloads as (byte[], InputStream, or String)
Sheet selection by sheet name.
Read the incoming payload safely as byte[]
Treat the XLSX file as a ZIP archive
Extract:
workbook.xml → map sheet name to sheet ID
sharedStrings.xml → resolve string values
sheetX.xml → actual worksheet data
Identify the correct worksheet by sheet name
Convert rows and cells into CSV format
Set CSV as the message body
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).
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:
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...🙂
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
| User | Count |
|---|---|
| 35 | |
| 9 | |
| 9 | |
| 5 | |
| 5 | |
| 5 | |
| 4 | |
| 4 | |
| 3 | |
| 3 |