How to fetch and display blob images
Asked Answered
V

2

9

I am trying to save images to indexeddb and then fetch and display them in a react app. My approach is to convert images to a blob and save the blob url to indexeddb and then when it's time to display, fetch the blob url and set the image url as .src.

How I created the blob

fetch(imgurl, {
                mode: 'no-cors',
                method: "get",
                headers: {
                     "Content-Type": "application/json"
                } 
             }) 
            .then(response => response.blob())
            .then(async images => {
               
                    var blob_url = URL.createObjectURL(images)
                  
                    var blobA = blob_url.replace("blob:","") 
                    var blobB = blobA.replace('"','')

this is bloburl blob:http://localhost:3000/d7a55f39-b496-476b-8c3c-857874bd107c

I then remove blob: and save this url in indexeddb to be fetched when needed http://localhost:3000/d7a55f39-b496-476b-8c3c-857874bd107c

this is where I need to use the blob image

import React from "react";
import { Row, Col, Container, Image } from 'react-bootstrap';

   
function Item({ item }) { 
    const imgurl = item.productimg
    fetch(imgurl)
        .then(res => res.blob()) // Gets the response and returns it as a blob
        .then(async blob => {
            const test = await blobToImage(blob)  
            console.log("image test",test)
        
            let objectURL = URL.createObjectURL(blob);
           let myImage = document.getElementById('myImg')
           myImage.src = objectURL;
            
        });
       
    return (
        <div className="item" id={item.id}>


            <Row>
                <Col> 
                <Image  id="myImg" width="50" height="58" rounded />

                </Col>
                <Col xs={6}>
                    <div className="item-info">
                        <p>{item.name}</p>
                    </div>
                </Col>
                <Col>3 of 3</Col>
            </Row>
           

            <div className="item-actions">
                <button className="btn-remove">X</button>
            </div>
        </div>
    );
}

export default Item;

Item contains all the productdata including the saved blob url. The problem is the images are not showing and I could not figure out why.

what could i be doing wrong and how can I display the images

Vargas answered 12/3, 2021 at 13:39 Comment(0)
L
7

Don't modify the Blob's URL.

When you took the blob: part out of the URL string to the Blob, the browser no longer knew that you were referencing your Blob. Instead, try leaving the URL alone after creating it with URL.createObjectURL.

On top of that, when synchronizing a component with an external resource (like fetching data), you should use React's useEffect hook.

Example with React

You can view this example live at StackBlitz.

function Item({ item }) {
  const imgURL = item.productimg;
  const [blobURL, setBlobURL] = React.useState(null);

  React.useEffect(() => {
    const controller = new AbortController(); // https://developer.mozilla.org/en-US/docs/Web/API/AbortController
    const signal = controller.signal;
    fetch(imgURL, { signal })
      .then((res) => res.blob()) // Get the response and return it as a blob
      .then((blob) => { // Create a URL to the blob and use it without modifying it.
        setBlobURL(URL.createObjectURL(blob));
      })
      .catch((error) => { // Error handling here
        console.error(error);
      });

    // Cancel the fetch request on any dependency change
    return () => controller.abort();
  }, [imgURL]);

  return (
    <div className="item" id={item.id}>
      <Row>
        <Col>
          {blobURL ? <Image src={blobURL} id="myImg" width="50" height="58" rounded /> : <p>Loading...</p>}
        </Col>
        <Col xs={6}>
          <div className="item-info">
            <p>{item.name}</p>
          </div>
        </Col>
        <Col>3 of 3</Col>
      </Row>

      <div className="item-actions">
        <button className="btn-remove">X</button>
      </div>
    </div>
  );
}

Example with plain JavaScript

const imgURL = "https://cdn62.picsart.com/182788826000202.jpg?type=webp&to=crop&r=256"
fetch(imgURL)
  .then(response => response.blob())
  .then(blob => {
    document.getElementById('my-img').src = URL.createObjectURL(blob)
  })
<img id="my-img" />

Good Luck...

Lafontaine answered 12/3, 2021 at 17:37 Comment(3)
I have this error when I use this.Cannot set property 'src' of undefined ...why?Clos
The useEffect in this solution needs work. It doesn't list igmurl as its dependency, and if it did, you would also need to add an AbortController to stop imageRef being modified when imgurl changes rapidly.Footrest
Hi @Andria, thanks for that!! I had no idea that AbortControllers existed!! I've also updated the deps for useEffect. A bonus is that I added the missing useMemo...Thanks once again :)Lafontaine
D
3

The URL lifetime is tied to the document in the window on which it was created.. ObjectURL is not meant to be saved somewhere. You can convert your image to base64 and store as data-uri instead.

Deutsch answered 12/3, 2021 at 17:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.