javascriptnode.jsangularexpressldapjs

Render an image byte stream on angular client side app


I have a NodeJS / Express RESTful API that proxies requests from an Active Directory LDAP Server. I do this because LDAP queries tend to be slow. I use the RESTful API to cache and refresh data intermittently. I recently attempted to add the thumbnail photo. In research it appears the library that I am using ldapjs is converting the native ldap byte array to a string.

Example of what this looks like:

\ufffd\ufffd\ufffd\ufffd\u0000\u0010JFIF\u0000\u0001\u0000\u0001\u0000x\u0000x\u0000\u0000\ufffd\ufffd\u0000\u001fLEAD Technologies Inc. V1.01\u0000\ufffd\ufffd\u0000\ufffd\u0000\u0005\u0005\u0005\b\

Due to this fact the image does not render correctly on the angular client app. So based on my research, here are some attempts in correcting this problem:

html binding:

<div>
  <img *ngIf="userImage" [src]="userImage" alt="{{dataSource.sAMAccountName}}">
</div>

controller:

public get userImage() {

  let value = null;
  if(this.dataSource.thumbnailPhoto) {

    const byteArray = this.string2Bin(this.dataSource.thumbnailPhoto);
    const image = `data:image/jpeg;base64,${Buffer.from(byteArray).toString('base64')}`;
    value = this.domSanitizer.bypassSecurityTrustUrl(image);
  }
  return value;
}

private string2Bin(str) {
  var result = [];
  for (var i = 0; i < str.length; i++) {
    result.push(str.charCodeAt(i));
  }
  return result;
}

and

alternate version of controller:

public get userImage() {

  let value = null;
  if(this.dataSource.thumbnailPhoto) {
    const byteArray = new TextEncoder().encode(this.dataSource.thumbnailPhoto);
    const image = `data:image/jpeg;base64,${Buffer.from(byteArray).toString('base64')}`;
    value = this.domSanitizer.bypassSecurityTrustUrl(image);
  }
  return value;
}

another alternate version of controller:

public get userImage() {

  let value = null;
  if(this.dataSource.thumbnailPhoto) {
    const blob = new Blob( [Buffer.from(this.dataSource.thumbnailPhoto).toString('base64')], { type: 'image/jpeg' } );
    const value = window.URL.createObjectURL(blob);
    return value;
}

I expected a rendered image on the angular page but all I get is the non-rendered placeholder.

Here are the versions of the libraries I am using

I am sure I am missing something, I am just not sure what it is. Any assistance would be appreciated.


Solution

  • So after some guidance provided by @Aritra Chakraborty, I checked the RESTful api source code. It appears to be a problem with a ldapjs library. When using the entry object conversion, it is doing something strange with the data to which it is not usable. I then realized, I had access to the entry raw format which is the byte array . Instead of trying to convert to base64 on the client, I moved this to the API. Then just mapped it back on the client binding and bang it worked.

    Here is some example code:

    RESTFul api

    _client.search(this._search_dn, opts, (error, res) => {
              res.on("searchEntry", (entry) => {
    
                let result = {};
    
                result.id = string_service.formatGUID(JSON.parse(JSON.stringify(entry.raw)).objectGUID);
                result = Object.assign({}, result, entry.object);
                if(entry.raw.thumbnailPhoto) {
                  result.thumbnailPhoto = entry.raw.thumbnailPhoto.toString('base64');
                }
    // The previous 3 lines did not exist previously
    

    On the Angular 8 client I simplified the binding:

    public get userImage() {
      let value = null;
      if(this.dataSource.thumbnailPhoto) {
        const image = `data:image/jpeg;base64,${this.dataSource.thumbnailPhoto}`;
        value = this.domSanitizer.bypassSecurityTrustUrl(image);
      }
      return value;
    }
    

    I hope someone finds some value out of this.