0

I have an API which returns a Product object with an ImageModel attached to it which is an image stored in a MYSQL database table. I understand that this may be simple but I have spent the last 36 hours pulling my hair out trying to figure what is going on here!

So I want to basically display products on an ecommerce site I am creating along with the product image. The API returns the products correctly aswell as the image data, however the front end wont display the image, just the product data.

Here is the response from the API:

REST API Response

Each object in the response list contains the same, just populated with different product data of course. Now, as far as I am aware, I should be able to pass the imageModel data to javascripts filereader to convert it to an image, but it does not seem to work.

This is the code:

    getProductsByCategory(obj) {

    let request ={
      "cat_id":obj.id
    }

   this.http.postRequestWithToken('api/product/getProductsByCategory',request).subscribe(data=>{
      this.productsList = data
      console.log('Prod List = ', this.productsList);
      if(this.productsList.length == 0){
        alert("No Product is found..");
      }

      this.productsList.forEach(product => {
            const reader = new FileReader();
            reader.readAsDataURL(product.imageModel);
            reader.onloadend = () => {
            product.images = reader.result;
         }
      });
   },error=>{
     alert("Error in login "+error);
   })
  }

productsList is the data shown in the console which definitely exists every time the API call is made. I then loop over the productList data and try to read the image data using readDataAsUrl which then gives the error:

Blob Error

My goal is to just simply display the image along with the product data in the HTML as follows:

<div id="myTabContent" class="tab-content">
          <div role="tabpanel" class="tab-pane fade active in" id="home" aria-labelledby="home-tab">
            <div   class="agile_ecommerce_tabs">
              <div  *ngFor="let data of productsList" class="col-md-4 agile_ecommerce_tab_left">
                <div class="hs-wrapper">
                  <img src="***{{data.image}}***" alt=" " class="img-responsive" />
                  <div class="w3_hs_bottom">
                    <ul>
                      <li>
                        <a href="#" data-toggle="modal" data-target="#myModal"><span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span></a>
                      </li>
                    </ul>
                  </div>
                </div>
                <!-- <h5><a href="single.html">{{data.name}}</a></h5> -->
                <h5><a style="cursor:pointer;" (click)="product_btn(data.id)">{{data.name}}</a></h5>

                <div class="simpleCart_shelfItem">
                  <p>
                    <span>{{data.price}}</span> <i class="item_price">£ {{data.price}}</i>
                  </p>
                    <button (click)="addCart(data)" type="submit" class="w3ls-cart">Add to cart</button>
                </div>
              </div>
              <div class="clearfix"> </div>
            </div>
          </div>

Note the line: <img src="***{{data.image}}***" alt=" " class="img-responsive" /> will obviously not work but if possible i would like to do this, so then every product is matched with its correct image.

I have consulted various different posts to try and help me achieve this such as:

Images in Angular

I have also tried writing a method which then gets passed into the loop previously shown but again does not work as i would like:

createImageFromBlob(image: Blob) {
    let reader = new FileReader();
    reader.addEventListener("load", () => {
    this.imageToShow = reader.result;
    }, false);

   if (image) {
      reader.readAsDataURL(image);
   }
}

I keep going round in circles and nothing seems to work! Im guessing im missing something very simple. If anyone has any ideas it would be appreciated!! Thanks for your time!


Edit 2


So, I have sanitized the url using the answer provided and also after consulting this Post I now get no errors in the console or my terminal (which is more annoying haha)

Updates:

I now loop over all of the productList and sanitize the image and add it back into the productsList as imageUpdate as follows:

    getProductsByCategory(obj) {
    let request ={
      "cat_id":obj.id
    }

   this.http.postRequestWithToken('api/product/getProductsByCategory',request).subscribe(data=>{
      this.productsList = data
      console.log('Prod List = ', this.productsList);

      if(this.productsList.length == 0){
        alert("No Product is found..");
      }

      this.productsList.forEach(product => {
        const sanitizedImage = this.sanitizer.bypassSecurityTrustUrl(`data:${product.imageModel.type};base64,${product.imageModel.picByte}`);
        product.imageUpdate = sanitizedImage;
      });

      this.productsList.forEach(element => {
        console.log('Final Updated Elements', element);
      });

   },error=>{
     alert("Error in login "+error);
   })
  }

This successfully updates the productList as follows:

Updated imageUpdate with SafeUrl

I then had to modify the html to:

<img [src]="data.imageUpdate" alt=" " class="img-responsive" />

to use the bind syntax.

However, on the website, the images still do not display :(

Front end display

I have no idea why this is happening!!

0

2 Answers 2

1

Your picByte is a base64 encoded string. You can simply pass this string to the image tag like

<img src="data:image/jpg;base64,R0lGODdhEAAQAMwAAPj7+FmhUYjNfGuxYYDJdYTIeanOpT+DOTuANXi/bGOrWj6CONzv2sPjv2CmV1unU4zPgI/Sg6DJnJ3ImTh8Mtbs00aNP1CZSGy0YqLEn47RgXW8amasW7XWsmmvX2iuXiwAAAAAEAAQAAAFVyAgjmRpnihqGCkpDQPbGkNUOFk6DZqgHCNGg2T4QAQBoIiRSAwBE4VA4FACKgkB5NGReASFZEmxsQ0whPDi9BiACYQAInXhwOUtgCUQoORFCGt/g4QAIQA7" />

(This example displays a check.)

You need to build this src string only, which should look like

<img src="data:{{product.imageModel.type}};base64,{{product.imageModel.picByte}}" alt=" " class="img-responsive" />

(I changed your data iteratee to product here to avoid confusions)


Edit:

You maybe need to sanitize the url. In this case you need to map your received product and add the following property:

constructor(private sanitizer: DomSanitizer) { }


const sanitizedImage = this.sanitizer.bypassSecurityTrustUrl(`data:${product.imageModel.type};base64,${product.imageModel.picByte}`);
Sign up to request clarification or add additional context in comments.

8 Comments

I will edit my answer, as this still does not work :/ Looks to be some kind of unsafe data which is preventing the image from displaying.
I have re-edited the answer. Still no luck. I tried sanitizing the data but i do not receive no errors anywhere! Thanks for your time!
I think your code looks good now, at least I can't find any further issue from my point of view. You could check the base64 string by an external program, in case it is corrupted or something E.g. base64.guru/converter/decode/image
Or maybe it is now a change detection issue only?
I have just used that link to check the base64 String and i get this back -> The MIME of file was detected as “application/octet-stream”, but the decoder displays it as “image/octet-stream”. Your browser cannot display the file as “image/octet-stream”. Do you have any ideas how I can get around this? Would the server need to send back a different MIME type? According to what i've read as long as the type is correct (data:image/jpeg;) the browser should display it
|
0

Ok so I figured this one out. Turned out to be a much simpler solution than I was originally trying to implement. I modified the API endpoint to return a byte[] and produce MediaType.IMAGE_JPEG_VALUE which directly serves the image as a resource which the browser can display correctly. Here is the code:

@GetMapping(value = "/getImage/{imageId}", produces = MediaType.IMAGE_JPEG_VALUE)
    public byte[] getImage(@PathVariable("imageId") String imageId) throws IOException {
        final Optional<ImageModel> retrievedImage = imageRepository.findById(Long.valueOf(imageId));

        ImageModel img = new ImageModel(retrievedImage.get().getName(), retrievedImage.get().getType(),
                decompressBytes(retrievedImage.get().getPicByte()));
        
        return img.getPicByte();
    }

This method basically returns the byte[] from the database. From the front end i can now just call the endpoint resource and it returns the image as expected:

 <img src="{{baseUrl}}/api/product/getImage/{{data.imageModel.id}}" alt=" " class="img-responsive" />

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.