Streaming binary data from salesforce blob into response

#1

Hey everyone, I come from a python background and I was wondering what would the correct way be to take a buffer that was streamed from a salesforce blob (using jsForce) into a Adonis response that will return a .pdf?

This is what I have so far. The code is a part of one controller I’m using to retrieve and stream the pdf. The issue I’m having is that the response is returned before any of the data is received. Please excuse my code if it violates any syntax standards (python bg here), I’m open to any suggestions. Thank you!

  async getInvoiceData( attachmentBodyId ) {
    // jsForce connection
    let conn = new jsforce.Connection({
      oauth2 : {
        loginUrl: Env.get('SF_LOGIN_URL', ''),
        clientId: Env.get('SF_CLIENT_ID', ''),
        clientSecret: Env.get('SF_CLIENT_SECRET', ''),
        redirectUri: Env.get('SF_REDIRECT_URL', '')
      }
    })

    conn.login(Env.get('SF_EMAIL', ''), Env.get('SF_PASSWORD', ''), function(err, userInfo) {
      if (err) { console.error(err) }

      let blob = conn.sobject('ContentVersion').record(attachmentBodyId).blob('VersionData')
      let bufs = []
      let buff = null
      blob.on('data', function (data) {
        bufs.push(data)
      })
      blob.on('end', function() {
        var buffer = Buffer.concat(bufs)
        console.log("GOT THAT DATA")
        return buffer
      })
    })
  }

  async downloadInvoiceAttachment ({ request, response }) {
    const attachmentBodyId = request.params.attachmentBodyId
    var pdfBuffer = await this.getInvoiceData(attachmentBodyId).toString('base64')
    try {
      console.log("RESPONSE SENT")
      return response.status(200).type('application/pdf').send(pdfBuffer)
    } catch (err) {
      return response.status(400)
    }
  }
1 Like

#2

I wrapped my streamed data into a Promise and used .then. So now my endpoint looks like this:

async downloadInvoiceAttachment ({ request, response }) {
    const attachmentBodyId = request.params.attachmentBodyId
    this.getInvoiceData(attachmentBodyId).then( function (data) {
      console.log("We got it?", data)
      try {
        return response.status(200).type("application/pdf").send(data.toString('base64'))
      } catch (err) {
        console.error(err)
        return response.status(400)
      }
    })
  }

I can confirm that the buffer is present, however it looks like as soon as the GET request is made, the API serves up a 204 and I encounter the

Error: Can't set headers after they are sent.
0 Likes

#3

Hi @Tuss4

Problem is, that you don’t await for this.getInvoiceData to finish.

Code throws up Promise on line 3: getInvoiceData and downloadInvoiceAttachment will return nothing.
After returning nothing back to client Promise in getInvoiceData will resolve into sending statuses 200 or 400.
Since nothing already went back to client you can’t add more data to sent request thus error is thrown

1 Like

#4

@McSneaky first and foremost, thank you for your reply!
Why would the endpoint return a 204 when the statuses that are declared are 200 and 400?
I thought the .then method was good to handle the async data coming from a promise, but I’ll try awaiting it.

EDIT: await did the trick!!! Thank you @McSneaky!!!

0 Likes

#5

Hi @Tuss4

Because when controller function returns returned data is sent to client

In current case controller function returns nothing / undefined and thus 204 - No content is sent to client

1 Like