I’m currently working on a Loopback API where I need to display images stored in an Object Storage service.
Here’s a high-level overview of the process I’m following:
The backend receives a request for a specific image.
It fetches the image from the Object Storage and sends it as a stream in the response.
I am receiving an empty response - 204, and when I check the LoopBack Explorer, there is an icon in the response body indicating that the image cannot be loaded.
Upon inspection, I found that the image source in the Loppback Explorer is <img src="blob:http://localhost:3000/b9f90a60-4909-4aa2-8ce8-c59313516bb8">.
Here is my code:-
import { ObjectStorageClient, UploadManager } from 'oci-objectstorage';
import { Readable } from 'stream';
import { basename } from 'path';
import common = require('oci-common');
import { Response }from '@loopback/rest';
import { TextDecoder } from 'util';
export class CloudStorageService {
constructor() {}
private createAuthenticationProvider() {
const tenancy = process.env.OCI_TENANCY_ID + '';
const user = process.env.OCI_USER_ID + '';
const fingerprint = process.env.OCI_FINGERPRINT + '';
const passphrase = null; // optional parameter
const privateKey = process.env.OCI_PRIVATE_KEY + '';
const region = common.Region.AP_SYDNEY_1;
return new common.SimpleAuthenticationDetailsProvider(
tenancy,
user,
fingerprint,
privateKey,
passphrase,
region
);
}
async getImageAsStream(folderName: string, imageName: string): Promise<Readable | null> {
try {
const authenticationProvider = this.createAuthenticationProvider();
const objectStorageClient = new ObjectStorageClient({
authenticationDetailsProvider: authenticationProvider,
});
const objectName = folderName + '/' + imageName;
const getObjectRequest = {
namespaceName: process.env.OCI_NAMESPACE + '',
bucketName: process.env.OCI_BUCKET_NAME + '',
objectName: objectName,
};
const getObjectResponse = await objectStorageClient.getObject(getObjectRequest);
console.log(getObjectResponse);
console.log(getObjectResponse.value);
if (getObjectResponse && getObjectResponse.value) {
const stream = Readable.from(getObjectResponse.value, {
objectMode: true,
highWaterMark: 16 * 1024,
});
return stream;
}
return null;
} catch (error) {
console.error('Error fetching image stream:', error);
return null;
}
}
}
And my Controller:-
import { get, param, Response, RestBindings,
} from '@loopback/rest';
import { inject } from '@loopback/core';
import { CloudStorageService } from '../utilities/oci.storage-service'; // Make sure to adjust the import path
export class ImageController {
constructor(
@inject('services.CloudStorageService')
private cloudStorageService: CloudStorageService,
) {}
@get('/image-stream/{folderName}/{imageName}')
async getImageStream(
@param.path.string('folderName') folderName: string,
@param.path.string('imageName') imageName: string,
@inject(RestBindings.Http.RESPONSE) response: Response,
): Promise<void> {
const imageStream = await this.cloudStorageService.getImageAsStream(folderName, imageName);
console.log("-----------------");
console.log(imageStream);
if (imageStream) {
response.setHeader('Content-Type', 'image/png'); // Set appropriate content type
imageStream.pipe(response); // Pipe the stream directly into the response
} else {
response.status(404).send('Image not found');
}
}
}
I suspect that I might be mishandling the stream or Blob creation on the frontend, or there might be something I’m missing in the interaction between the backend and frontend. Could you please guide me on the proper approach to fetch and display these images correctly?