Images from backend are not displayed

Issue

The image that I transfer from a nestjs backend to a angular frontend does not appear in the browser.

enter image description here

I send a query from the front to the backend containing the path to the file on the server (for example: "C:\MyFolder\MyFile.png"), which will read the image and send a stream back to the frontend. The frontend will then convert the blob to an image and set an img element’s src attribute to the URL of the image.

Backend

The backend code of the controller:

@HttpCode(201)
@Get(':imageUrl')
@Header('Content-Type', 'image/png')
getFile(@Param('imageUrl')imageUrl: string) {
    imageUrl = atob(imageUrl);
    return fs.createReadStream(imageUrl);
}

Frontend

Language selector ts code:

currentLanguage: ApplicationLanguage;
availableLanguages: ApplicationLanguage[];

constructor(private readonly _languageProvider: CultureProviderService,
          private readonly _imageLoader: ImageService) {
    this.currentLanguage = _languageProvider._currentLanguage;
    _languageProvider.languageChanged.subscribe(
      newLanguage => {
        this.currentLanguage = newLanguage;
      }
    )
}
ngOnInit(): void {
    this._languageProvider.getAllAvailableLanguages().subscribe(
      (languages: ApplicationLanguage[]) => {
        this.availableLanguages = languages;
        languages.forEach((language: ApplicationLanguage) => {
          this._imageLoader.getImage(language.flagIcon).subscribe(
            imgBlob => {
              this._imageLoader.createImageFromBlob(imgBlob).then(
                result => {
                  language.image = result;
                },
                err => {
                  console.log(err);
                }
              )
            }
          );
        });
      },
      err => {
        console.log(err);
      }
    );
}

The service that retrieves the image:

constructor(private readonly _http: HttpClient,
          private domSanitizer: DomSanitizer) { }

getImage(imageUrl: string): Observable<Blob> {
    return this._http.get(`${environment.machineServerUrl}/file/${btoa(imageUrl)}`, { responseType: 'blob' });
}

createImageFromBlob(imageUrl: Blob): Promise<SafeUrl> {
    return new Promise<SafeUrl>((resolve, reject) => {
      const reader = new FileReader();
      reader.addEventListener(
        "load",
        () => {
          resolve(this.domSanitizer.bypassSecurityTrustUrl(reader.result as string));
        }
      );

      if (imageUrl) {
        reader.readAsDataURL(imageUrl);
      } else {
        reject();
      }
    });
}

The HTML:

<div>
  <mat-selection-list (selectionChange)="changeLanguage($event)"
                      [multiple]="false">
    <mat-list-option  *ngFor="let language of availableLanguages"
                      [value]="language"  [selected] ="isSelected(language)">
      <img [src]="language.image" height="70px" width="70px" alt="Flag">
    </mat-list-option>
  </mat-selection-list>
</div>

Did I do something wrong? Is something missing?

Edit:

The solution thanks to the accepted answer

First, nestjs version needed to be upgraded to v8+: nest update –force. Unfortunately it failed for my project, so I deleted dist, node_modules and package-lock.json, created a new nestjs project using CLI v8+ and used the generated package.json file to upgrade the project.

Then, updated the controller’s code to use StreamableFile as suggested in the accepted answer:

@HttpCode(201)
@Get(':imageUrl')
@Header('Content-Type', 'image/png')
getFile(@Param('imageUrl')imageUrl: string): StreamableFile {
  imageUrl = Buffer.from(imageUrl, 'base64').toString();

  const file = createReadStream(imageUrl);
  return new StreamableFile(file);
}

Official resource: https://docs.nestjs.com/techniques/streaming-files

Solution

If you’re going to be returning a stream, you’d need to do something like

@HttpCode(201)
@Get(':imageUrl')
@Header('Content-Type', 'image/png')
getFile(@Param('imageUrl')imageUrl: string, @Res() res: e.Response) {
    imageUrl = atob(imageUrl);
    return fs.createReadStream(imageUrl).pipe(res);
}

If you’re using Nest v8, you can use the StreamableFile and do something like

@HttpCode(201)
@Get(':imageUrl')
@Header('Content-Type', 'image/png')
getFile(@Param('imageUrl')imageUrl: string, @Res() res: e.Response) {
    imageUrl = atob(imageUrl);
    return new StreamableFile(fs.createReadStream(imageUrl));

And Nest will handle calling the .pipe(res) for you under the hood

Answered By – Jay McDoniel

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published