Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use binary image data from Web API as background-image of DOM element (Angular)

I'm having lots of trouble trying to get an image I'm retrieving from my Web API to display as a background-image in my Angular application.

The Web API's action that returns the image looks like this:

    /// <summary>
    /// Returns the medium image for the specified project as image/png media type.
    /// </summary>
    /// <param name="projectId"></param>
    /// <returns></returns>
    [HttpGet("{projectId:int}/image/medium")]
    [SwaggerResponse(200, typeof(byte[]))]        
    [Produces("image/png")]
    public FileContentResult GetImageMedium(int projectId)
    {            
        var res = _repository.GetProjectResource(projectId, "Medium");
        FileContentResult result = new FileContentResult(res.ByteContent, res.MediaType);            
        return result;
    }

This is currently what my Angular service method looks like (but I have tried lots of alternatives):

  getProjectImage(id: number) {
    return this._http
      .get(`${this._url}/${id}/Image/Medium`, { headers: this._auth.headers, responseType: ResponseContentType.Blob })
      .map(response => response.arrayBuffer());
  }

And this is what I'm trying to apply to the [style.background-image] of the DOM element:

return `url(${'data:image/png;base64,' + new Buffer(response)})`

As I said before, I've been trying combinations of things from things I've found around the internet, but I have an inherent misunderstanding of most of the pieces involved. What is my Web API returning me? How can I handle that client side? How do I massage the content into something appropriate for a background-image css property? I would really like to understand this much better in addition to getting this to work. All help is greatly appreciated!

Update

I also just tried to change the Web API action to return a base64 string like this:

/// <summary>
/// Returns the medium image for the specified project as image/png media type.
/// </summary>
/// <param name="projectId"></param>
/// <returns></returns>
[HttpGet("{projectId:int}/image/medium")]
[SwaggerResponse(200, typeof(byte[]))]
[Produces("image/png")]
public string GetImageMedium(int projectId)
{
    var res = _repository.GetProjectResource(projectId, "Medium");
    return Convert.ToBase64String(res);
}

and handling it client side like this:

  getProjectImage(id: number) {
    return this._http
      .get(`${this._url}/${id}/Image/Medium`, { headers: this._auth.headers });
  }

and simplifying the caller of my service to provide the following to [style.background-image] of the DOM element:

return `url(${'data:image/png;base64,' + response})`

I've console.loged the response and it indeed looks like a base64 string (I think?). But the css never changes.

Update 2

My markup is currently this:

<div class="image"
     [style.background-image]="project.image | async"></div>

My component's code is currently this:

projects.map(project => {
  project.image = this._projectsService.getProjectImage(project.projectId)
    .map(response => `url('data:image/png;base64,${response['_body']}')`)
});

I've gotten to the point where if I console.log this out, copy it, and paste it into the css rules for the div in dev tools, the image shows up. I've tried running this in NgZone and the css still won't show up. Any and all help is appreciated.

Update 3

So, if I change <div class="image" [style.background-image]="project.image | async"></div> to <img [src]="project.image | async" alt="">, I get the image to appear asynchronously. The unfortunate thing about this approach is that while the request is in flight, the image error icon is visible. This error icon remains for any items that don't return an image (incomplete database)

like image 483
Jake Smith Avatar asked Jul 22 '17 18:07

Jake Smith


1 Answers

To avoid displaying the error image until the actual image if loaded, change your html to this:

<img *ngIf="project.image" [src]="project.image | async" alt="">

The solution in your Update 3 will also work. However, here is another way to do it. You can set the [style] directive of the div to add the background-image. You will have to sanitize the styles when passing them to the dom.

In your component.html:

<div class="image" [style]="backgroundImageStyle"></div>

.. to remove the error image until we get the image, we add an *ngIf:

<div class="image" *ngIf="backgroundImageStyle" 
                    [style]="backgroundImageStyle">
</div>

... and in your component.ts:

// ..
import { DomSanitizer } from '@angular/platform-browser';

// Declare a variable to store the sanitized style
private backgroundImageStyle;

// Initialize the constructor
constructor(sanitizer: DomSanitizer) { 
    // You can call this whereever you want, I am doing it in constructor
    this.backgroundImageStyle = this.getBackgroundImageStyle();
}

private getBackgroundImageStyle() {        
    // sanitize the style expression
    style = `background-image: url(${project.image})`;
    this.sanitizer.bypassSecurityTrustStyle(style);
}
// ..
like image 188
Faisal Avatar answered Sep 28 '22 06:09

Faisal