8

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)

Jake Smith
  • 2,332
  • 1
  • 30
  • 68
  • Did you find the solution for your question. I have exactly same issue (i.e. showing the binary image (from database via REST) as background in div – SK. Oct 08 '19 at 13:10

2 Answers2

3

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);
}
// ..
FAISAL
  • 33,618
  • 10
  • 97
  • 105
  • Thank you for your suggestion, but the `*ngIf` directive didn't work for me because `project.image` wasn't falsey. I'm not sure if this is completely correct, but the error icon remained because `project.image` is an observable and not falsey. I thought this would work. – Jake Smith Aug 16 '17 at 16:44
  • 1
    You are absolutely right about having to sanitize the styling using the `DomSanitizer` – Jake Smith Aug 16 '17 at 16:45
  • Actually it didn't help. This is pretty similar to the code I already had in my own attempts. But I did not mention the DomSanitizer in my question, which is something I had tried and had removed on subsequent attempts. I'm not sure what the issue is, but I'll keep you posted if I find out what the issue was. – Jake Smith Aug 16 '17 at 16:55
3

Why you cannot just put your link to img to background-image directly?

<div class="image"
     [style.background-image]="url('/your-url-to-img')"></div>

The browser will do your work for you.

You need to be sure that web server returns right image and headers

Anton Lee
  • 684
  • 4
  • 10
  • I wonder what I can do there....good idea. The image is in the database as binary data already, is there a way to get the png out of it server side? Is that worth it? – Jake Smith Aug 18 '17 at 04:14
  • If you already have binary data, then just write it to response with appropriate headers, like image/png or image/* – Anton Lee Aug 18 '17 at 04:34
  • 1
    I'll try tomorrow! Thanks! – Jake Smith Aug 18 '17 at 04:35
  • i agree. everything is actually just a bunch of binary data. i would recommend you to ponder whether to get it out of the DB though, it is just a waste of computing effort to manage it as far as i see it – MatanCo Aug 21 '17 at 14:47
  • 1
    @MatanCo, what do you mean by "ponder whether to get it out of the DB?" Do you mean I should have all these images locally? – Jake Smith Aug 21 '17 at 22:09
  • @AntonLee, I could not get this to work. I would prefer this option and I'll keep trying this. But so far, I can't get it to work – Jake Smith Aug 21 '17 at 22:10
  • @AntonLee, I think this is trying to look for a public method called `url` in my component and that's part of the problem I'm having. The url needs a project id in there, but it doesn't like interpolation the way I'm doing it. And it also complains about not know what the url method is – Jake Smith Aug 21 '17 at 22:24
  • `
    public imgUrl(id): string { return this._url + '/' + id + '/Image/Medium'; }`
    – Anton Lee Aug 22 '17 at 02:24