1

I think I have now tried everything and read every question on this matter, but still I can't make it to work..

Cart.vue

<template>
<div>
<h1>CART</h1>
  <img :src="imgSrc" style="width:600px; height: 600px">
</div>
</template>

Cart.vue mounted()

mounted(){
    const qr = firebase.functions().httpsCallable('sendPaymentRequest')
    qr()
      .then(res => {
        const blob = new Blob([res.data], {type: 'image/jpg'})
        console.log(blob);
        const url = (window.URL || window.webkitURL).createObjectURL(blob)
        console.log(url);
        this.imgSrc = url;
      })

Firebase functions

exports.sendPaymentRequest = functions.https.onCall((data, context) => {
const qr = async () => {
       try {
        const json = {
            token: "umP7Eg2HT_OUId8Mc0FHPCxhX3Hkh4qI",
            size: "300",
            format: "jpg",
            border: "0"
        }
        const response = await axios.post('https://mpc.getswish.net/qrg-swish/api/v1/commerce', json)
       console.log('status', response.status)
       if(response.status !== 200){throw new Error ('Error requesting QR code')}
        return response.data
    } catch (e){
        console.log('error', e)
        return e
    }
    }
    return qr();
})

In my 2 console logs in the mounted() hook - the Blob and the URL - I get: enter image description here

Looking pretty all right? There seem to be a Blob? And a URL?

however:

enter image description here

... sooo I tried changing the mounted() to

const qr = firebase.functions().httpsCallable('sendPaymentRequest')
qr()
.then(res => {
  const self = this;
  const blob = new Blob([res.data], {type: 'image/jpg'})
  const reader = new FileReader();
  reader.onloadend = function() {
    self.imgSrc = reader.result
  };
  reader.readAsDataURL(blob);
})
.catch(e => console.log(e))

which also seem to work but.. well it's not.. Now I got a nice little base64-encoded string to my image instead of URL: enter image description here

But still no image..

So I tried some other stuff I found while reading all of Internet.. moving from a callable function to onRequest function etc.. When I'm doing the exact same request with Postman I'm getting a fine QR code in the response..

If I'm loggin the response.headers in firebase functions I'm seeing

'content-type': 'image/jpeg', 'content-length': '31476',

So on the server I'm getting an image.. which I'm sending with return response.data

response.data being:

����JFIF��C

$.' ",#(7),01444'9=82<.342��C

2!!22222222222222222222222222222222222222222222222222�,,"��

and so on..

And that's where I'm at.. I'm getting .. frustrated.

Does anyone on here see what I'm doing wrong??

EDIT 1 for anyone running into this in the future - as @Kaiido points out on client I have to add

  ...
  responseType: "blob"
}

but also on server, with firebase you need to move from

functions.https.onCall(async (data, context) => {

to

functions.https.onRequest(async (req, res) => {

call it on client with:

axios({
        method: 'get',
        url: 'http://localhost:5001/swook-4f328/us-central1/retrieveQr',
        responseType: 'blob',
       })
        .then(async res => {
          const url = (window.URL || window.webkitURL).createObjectURL(res.data)
          this.imgSrc = url;
          
        })
        .catch(e => e)

and on server instead of axios use request (for some reason, but this works.. no idea why, but solves problem for now though I would be curious to why and I prefer axios to request)

works

const json = {
        token: "umP7Eg2HT_OUId8Mc0FHPCxhX3Hkh4qI",
        size: "300",
        format: "png",
        border: "0"
    }
var requestSettings = {
    url: 'https://mpc.getswish.net/qrg-swish/api/v1/commerce',
    method: 'POST',
    encoding: null,
    json: true,
    'content-type': 'application/json',
    body: json,
};

request(requestSettings, (error, response, body) => {
    res.set('Content-Type', 'image/png');
    res.header({"Access-Control-Allow-Origin": "*"});
    res.send(body);
});

does not work

const json = {
    token: "umP7Eg2HT_OUId8Mc0FHPCxhX3Hkh4qI",
    size: "300",
    format: "png",
    border: "0"
}
const response =  await axios.post('https://mpc.getswish.net/qrg-swish/api/v1/commerce', json)
if(response.status !== 200){throw new Error ('Error requesting QR code')}
res.header({"Access-Control-Allow-Origin": "*"}).writeHead(200, {'Content-Type': 'image/png'}).end(response.data)
// neither this works:
// res.header({"Access-Control-Allow-Origin": "*"}).status(200).send(response.data)
JohnAl
  • 113
  • 1
  • 11

1 Answers1

2

You are receiving an utf8 encoded text, some bytes from the binary response have been mangled.

When doing your request, add an extra

  ...
  responseType: "blob"
}

option to your axios request, this will ensure the binary data is not read as text but preserved correctly.
Plus now you don't need to build the Blob yourself, it is already one in response.data.

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • Thank you! I'm getting one step closer but not there.. By doing this, I now receive a Blob in my "data", but the content-type of the Blob is "text/html", although I am from my server sending the response of the API call straight out, which is a "image/jpeg".. I have moved from httpsCallable to a "regular" https function, which I request using axios from client. Bc CORS I cannot call the 3rd party API straight from client, but basically server is just making the call, returning response but somehow it converts to text/html when transferred.. sooo close now!! Thanks a ton! – JohnAl Oct 05 '20 at 08:45
  • @JohnAlfredsson and if you do `console.log( await blobtext() )` you still get the correct `����JFIF��C` content? If so, you should fix the server-side to send the correct heders, but you could always force the mime type by doing `new Blob([blob], { type: "image/jpeg" });` – Kaiido Oct 05 '20 at 09:09
  • correct, blob.text() get me that content.. I've just figured out though on the server I should do `res.header({"Access-Control-Allow-Origin": "*"}).writeHead(200, {'Content-Type': 'image/jpeg'}).end(response.data)`instead of `res.status(200).send(response.data)` - this gives me the Blob as image/jpeg on client.. but it still doesn't render.. Do I need to read the response.data on server as a file using fs or something? Or writeStream.. Thanks a lot for your help, I'm a beginner to this, means a lot! – JohnAl Oct 05 '20 at 09:41
  • All right! I got it to work. `responseType: "blob"` has to be set on client.. But 2nd part of what doesn't work is on my server it seems I'm doing something wrong with axios, as I got it to work with request.. why I'm as of now unsure, reading up on it.. Noticing the blob size in client when using axios on server is 96292 while on request 53453.. there's gotta be something wrong with my axios call / response.. – JohnAl Oct 05 '20 at 11:47
  • edited my original question to include code examples.. will mark this answered as you did answer, the axios/request thing seem to be a separate question! Many thanks! – JohnAl Oct 05 '20 at 12:01
  • ...and for my future self: ResponseType blob is only client - setting to 'arraybuffer' on server makes it work with axios.. as @Kaiido has pointed out in [this](https://stackoverflow.com/a/60461828/7421690) post.. ;) – JohnAl Oct 07 '20 at 19:18