Search code examples
angularimagerestblobfilereader

Java REST API returns object with image attached but will not display in Angular Front End


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!!


Solution

  • 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" />