cancel
Showing results for 
Search instead for 
Did you mean: 

Returning Excel with a CAP service

TheWind
Explorer
0 Kudos
242

Hello, I am trying this feature in order to download a dynamically generated excel from a UI5 app: https://cap.cloud.sap/docs/node.js/best-practices#custom-streaming-beta

The way I use it is this:

const readable = Readable.from(buffer)
return {
  value: readable,
  $mediaContentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  $mediaContentDispositionFilename: `${fileName}.xlsx`,
}

Where readable is of type stream.Readable and the buffer comes from ExcelJS.

When I trigger it in the browser, it correctly downloads an attachment of type XLSX, but the content is:

{
@odata.context: "$metadata#Edm.Binary"
value: {
  value: "[object Readable]"
  $mediaContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  $mediaContentDispositionFilename: "DCT - 212 - TEST TAX - SSD 2024-10-05.xlsx"
  }
}

So the contents of the stream are not actually read, but rather, the stream object is somehow converted into a string. I found a bunch of other community posts regarding media data, but only few of them got clear answers. Am I making a mistake here, or is this a bug? The documentation states no requirements for the streamed data, only that the type of the object must be stream.Readable

View Entire Topic
catano
Active Participant
0 Kudos

Hi @TheWind 
I tried it with version 8.3 and it works for me.
I added my media field as LargeBinary type in the schema, in my case with a fixed media type, but this can be made dynamic as described in the documentation:

entity Books { // ...
        attachmentXlsx : LargeBinary @Core.MediaType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
}

I see it as Edm.Stream in the metadata.

<EntityType Name="Books">
  <Key>
    <PropertyRef Name="ID"/>
  </Key>
  <Property Name="ID" Type="Edm.Int32" Nullable="false"/>
  <Property Name="attachmentXlsx" Type="Edm.Stream"/>
</EntityType>

I used the custom read logic as follows, I used the createReadStream from Node.js fs to read a static file:

  srv.on('READ', 'Books', (req, next) => {
    if (attachmentIsRequested(req)) {
      const readable = fs.createReadStream('files/test.xlsx');
      return {
        value: readable,
        $mediaContentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        $mediaContentDispositionFilename: 'test.xlsx', // > optional
        $mediaContentDispositionType: 'inline' // > optional
      }
    }
    return next()
  });

By typing the following URL into my browser, I was able to download and open the Excel file, and the content was correct (according to the ID, this should point to an existing record):

http://localhost:4004/odata/v4/galactic/Books(201)/image

It might be worth trying a direct browser link before the UI download link so that it doesn't interfere with the testing.