10

I'm fetching an image from SAP B1 Service Layer. In postman, I'm able to view it as image/png, but there is an issue displaying it.

What's the correct way to show it in <img />?

require(fetchedImage) - doesn't work


I have created a Cloud Function to fetch the image and pass it on to the client, but I am not sure how to do it.

Having a super weird object something like this

 data:
>     '�PNGörönöu001aönöu0000öu0000öu0000örIHDRöu0000öu.........

Don't know how to pass it via res.send(IMAGE IN PNG) so I can see get an image on the client-side.

Checked base64 conversion but I'm not sure how to use them.


Update

Postman Request: (This is working fine)

GET : https://su05.consensusintl.net/b1s/v1/ItemImages('test')/$value

Headers: SessionId: ASK ME WHEN YOU TRY

For some reason, we can't fetch the Image directly in Front-End and need to create a middleware so we're doing it in Firebase Cloud Function

So here is the function which fetches the image and doesn't know how to pass it.

Here is the function in Firebase Cloud Function:

if (!req.body.productId) {
      res.status(400).send({ error: "productId is required" });
      return;
    }

    console.log("Starting the process");

    const productId = req.body.productId;

    const login = await Auth.login();
    const fetchedImg = await ItemMaster.getImage(login["SessionId"], productId);

    //Here in the fetchedImg, we're getting some data like
    res
      .status(200)
      .set("Content-Type", "image/png")
      .send(fetchedImg);

And we're getting a response like this:

{ status: 200,

statusText: 'OK',

headers:

{ server: 'nginx',

  date: 'Wed, 22 Jan 2020 03:52:22 GMT',

  'content-type': 'image/png',

  'transfer-encoding': 'chunked',

  connection: 'close',

  dataserviceversion: '3.0',

  'content-disposition': 'inline; filename="rr-96600.png"',

  vary: 'Accept-Encoding',

  'set-cookie': [ 'ROUTEID=.node2; path=/b1s' ] },

config:

{ url:

data:

'�PNG\r\n\u001a\n\u0000\u0000\u0000\rIHDR\u0000\u0000\u0000�\u0000\u0000\u0000�\b\u0002\u0000\u0000\u0000\u0006\u001fS�\u0000\u0000\u0000\u0019tEXtSoftware\u0000Adobe ImageReadyq�e<\u0000\u0000\u0003hiTXtXML:com.adobe.xmp\u0000\u0000\u0000\u0000\u0000

THIS IS SUPER LONG AND GOES FOR 80-100 more lines

If you want to test you can use the following:

Postman:

POST: https://us-central1-rapid-replacement.cloudfunctions.net/getImageFromItems

body: {"productId":"test"}

Valid productId are: 1. "RR000102" 2. "test" 3. "RR000101"

Boghyon Hoffmann
  • 17,103
  • 12
  • 72
  • 170
Dhaval Jardosh
  • 7,151
  • 5
  • 27
  • 69

3 Answers3

4

If you want to use images dynamically, you have to fetch the images as soon as the component is mounted and insert it afterwards. The fetched picture should then be saved in the state of the component and included from there in the src attrbut of the img tag. Assuming you can already fetch the picture, the code below should work.

import React, { Component } from "react";

export default class ComponentWithFetchedImage extends Component {
  constructor() {
    super();
    this.state = { image: undefined };   
  }

  componentDidMount() {
    let fetch_url = "https://picsum.photos/200";   // Insert your fetch url here
    fetch(fetch_url)
      .then(res => res.blob())
      .then(blob => URL.createObjectURL(blob))
      .then(url => this.setState({ image: url }))
      .catch(err => console.log(err));
  }

  render() {
    return (
      <div className="component">
        <img src={this.state.image} alt="" />
      </div>
    );   
  }
}
Taric
  • 41
  • 4
  • 1
    I can't fetch directly in ComponentDidMount, as I need to make a custom function on the Server-Side and for that, I'm doing it via Cloud function. – Dhaval Jardosh Jan 21 '20 at 00:33
  • 1
    It's not allowing me to fetch on client side, it says illegal headers passed in fetch / axios, when I do it from React. – Dhaval Jardosh Jan 21 '20 at 19:46
2

Here is the closest to a working solution I came. Basically what I tried is to fetch the image and then turn it into a blob on the client so you can make it into a objectURL.Updated code streams the image as a buffer and consumes it on the client then turns it into objectURL and assigns to image src

Server code:

const http = require('http')
const axios = require('axios')
const fs = require('fs')
const stream = require('stream')
const server = http.createServer(function(req, res) {
  if (req.url === '/') {


    res.setHeader("Access-Control-Allow-Origin", "*");
    axios.post(
      "https://su05.consensusintl.net/b1s/v1/ItemImages('test')/$value", {
        responseType: "blob"
      }).then(function(resp) {
      console.log(resp.data)
      const buf_stream = new stream.PassThrough()
      buf_stream.end(Buffer.from(resp.data))
      buf_stream.pipe(res.end())
    }).catch(err => console.log(err))
  }
})


server.listen(3500)

Client code:

<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title></title>
</head>

<body>
  <img style="width:200px; height:200px" />
  <script>

  const img = document.getElementsByTagName("IMG")
  fetch('http://localhost:3500').then(function(response) {
    console.log(response)
    return response.body
  }).then(function(data) {
    console.log(data)
    const reader = data.getReader()
     return new ReadableStream({
    start(controller) {
      return pump();
      function pump() {
        return reader.read().then(({ done, value }) => {
          // When no more data needs to be consumed, close the stream
          if (done) {
              controller.close();
              return;
          }
          // Enqueue the next data chunk into our target stream
          controller.enqueue(value);
          return pump();
        });
      }
    }
  })
})
  .then(stream => new Response(stream))
  .then(response => response.blob())
  .then(blob => URL.createObjectURL(blob))
  .then(url => img[0].src = url)
  .catch(err => console.error(err));
    </script>
</body>

</html>
C.Gochev
  • 1,837
  • 11
  • 21
  • Hey one thing I forgot to tell you, that I do get an image here `GET : https://su05.consensusintl.net/b1s/v1/ItemImages('test')/$value`, but when I pass the same thing, it's not working. Let me know if you get a better idea, sorry if I'm bothering you. – Dhaval Jardosh Jan 26 '20 at 01:09
  • small appreciation from my side :) – Dhaval Jardosh Jan 26 '20 at 01:12
  • see updated code. this was my final attempt. hope you find a solution. – C.Gochev Jan 26 '20 at 12:26
1

This issue has been resolved.

const getImage = async (sessionId, ItemCode) => {
  console.log("fetching image");
  let result = {};

  result = await axios.get(
    `${settings.url}${endpoints.ItemImages}('${ItemCode}')/$value`,
    { 
      headers: {Cookie: `B1SESSION=${sessionId}`},
      responseType: "arraybuffer" } 
    ).then(response => Buffer.from(response.data, 'binary').toString('base64'));

  //Here we're returning base64 value of Image
  return result;
};

So we're able to see the image on the client-side using

<img src="data:image/png;base64,[BASE64-VALUE-HERE]"/>

Dhaval Jardosh
  • 7,151
  • 5
  • 27
  • 69