4

I am trying to get an image (PNG format) to display in a React app after making a JavaScript call. The code is as follows. The function DeviceService.getFile returns the file in a blob. The data is binary. How can I get this image to be displayed correctly in React?

I have tried the conversion to base64 but it did not help.

DeviceService.getFile(mapOverlayImagePath, bMap1 => {
        this.setState({ MapOverlay: bMap1 })
        // this.setState({ MapOverlay: 'data:image/png;base64,' + btoa(bMap1) })
        console.log(bMap1)
      })

The React code to display the image:

<img src={this.state.MapOverlay} alt="MapOverlay" />

I have modified this function, the function getFile is as follows:

export function getFile(path, cb) {
  if (typeof(cb) === 'undefined' || cb === null)
    return;

  fetch(uris.getFile() + '/?' +
    'path=' + path,
    {method: 'POST', credentials: 'include'})
    .then(reply => reply.blob())
    .then((response) => {
      if (response.data) {
        return cb(response.data);
      }
      return cb(new Blob());
    })
}

This getFile function is in a library where it is used as a dependency in the React application.

I tried printing the blob size in the Internet Explorer console and it displayed: [object Blob]: {size: 0, type: ""}. I guess my getFile function is not passing the data as intended. Any error in the getFile function?

Eugene
  • 1,013
  • 1
  • 22
  • 43
  • Change the code you're using to make the image request. Call `.blob()` on the Response object instead of arrayBuffer() to get the binary data in a way that is more directly and efficiently usable by the browser. Then you can ask the browser for a Blob URL, which you can use in an image's src. – Touffy Nov 28 '19 at 20:09
  • Editing questions in a way it invalidates already posted answers is frowned upon. Please rollback to the previous version and instead either post a comment on the answer asking for clarifications if you missed something, or add the new information in the question. – Kaiido Nov 29 '19 at 03:18
  • 1
    But here you simply didn't read the answer carefully => `.then( buffer => { // note this is already an ArrayBuffer // there is no buffer.data here`Same goes for the blob() version: the returned value is the Blob, so your `if(response.data)` will never pass, since what you want is what you miscalled `response`. So just do `.then(reply => reply.blob()).then(cb)` – Kaiido Nov 29 '19 at 03:20

2 Answers2

9

Make a Blob of this ArrayBuffer and make your image point to this Blob thanks to a blob-URI.

fetch( 'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png' )
  .then( r => r.arrayBuffer() )
  .then( buffer => { // note this is already an ArrayBuffer
    // there is no buffer.data here
    const blob = new Blob( [ buffer ] );
    const url = URL.createObjectURL( blob );
    const img = document.getElementById( 'img' );
    img.src = url;
    // So the Blob can be Garbage Collected
    img.onload = e => URL.revokeObjectURL( url );
    // ... do something else with 'buffer'
  } );
<img id="img">

But if you don't have a real need for an ArrayBuffer, then make the browser directly consume the Response as a Blob:

fetch( 'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png' )
  .then( r => r.blob() ) // consume as a Blob
  .then( blob => { 
    const url = URL.createObjectURL( blob );
    const img = document.getElementById( 'img' );
    img.src = url;
    // in case you don't need the blob anymore
    img.onload = e => URL.revokeObjectURL( url );
  } );
<img id="img">

But then, in your position I would even try to make a simple GET request directly from your <img> to uris.getFile() + '/?path=' + path.

Kaiido
  • 123,334
  • 13
  • 219
  • 285
0

I would like to post an answer using MERN stack.

I am already getting my object as an array buffer from my backend and passing it to my frontend.

Here is the Node function making an axios call in my backend to get my arrayBuffer

  const arrayBuff = await axios({
                        url:`https://api.twilio.com${msg.mediaUri.slice(0, -5)}`, 
                        method:"GET",
                        auth: { username: subActServiceSid, password: subActAuthToken },
                        responseType: 'arraybuffer'
                        }).then(res => { return res.data})

the object coming from the backend looks like this

media: {type: 'Buffer', data: Array(167802)}

in my component I am setting an image state with useEffect like this



useEffect(() => {
    if(media?.data) {
      setImage(Buffer.from(media.data, 'binary').toString('base64')) 
    }
  }, [])




Now when I want to view the image, I add it to an image tag like this

<img src={`data:image/*;base64,${image}`} className="img-fluid rounded" width={150} alt="" />

KingJoeffrey
  • 303
  • 5
  • 16