Issue
The image that I transfer from a nestjs backend to a angular frontend does not appear in the browser.
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