Processare immagini da Google AI Studio con Angular
Quello che vogliamo ottenere è inviare richieste HTTP a Google AI Studio, che fa parte delle Gemini API, per processare una immagine.
Invieremo anche un prompt; il tutto da Angular.
Il codice potrebbe cambiare un pò a seconda della versione di Angular; nel mio caso parliamo della versione 19.
Prima di tutto avete bisogno di una API KEY che potete creare direttamente in Google AI Studio.
Cominciamo dall'app.config.ts dove aggiungiamo un provider:
import {ApplicationConfig, provideZoneChangeDetection} from '@angular/core';
import {provideRouter} from '@angular/router';
import {routes} from './app.routes';
import {provideHttpClient, withFetch} from "@angular/common/http";
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({eventCoalescing: true}),
provideRouter(routes),
provideHttpClient(withFetch())
]
};
Questo il mio component:
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {Component} from '@angular/core';
import {JsonPipe, NgIf} from "@angular/common";
import {FormsModule} from "@angular/forms";
@Component({
selector: 'app-root',
imports: [JsonPipe, NgIf, FormsModule],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
private apiKey = '';
private apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${this.apiKey}`;
promptText: string = "";
selectedFile: File | null = null;
imageBase64: string | null = null;
analysisResult: any = null;
isLoading: boolean = false;
errorMessage: string | null = null;
rawAiResponseForDebug: string | null = null;
constructor(private http: HttpClient) {
}
onFileSelected(event: any): void {
const file: File = event.target.files[0];
if (file) {
this.selectedFile = file;
this.errorMessage = null;
this.analysisResult = null;
this.rawAiResponseForDebug = null;
const reader = new FileReader();
reader.onload = (e: any) => {
this.imageBase64 = e.target.result.split(',')[1];
};
reader.readAsDataURL(file);
}
}
analyzeImage(): void {
if (!this.selectedFile || !this.imageBase64) {
this.errorMessage = "Per favore, seleziona un'immagine.";
return;
}
if (!this.promptText.trim()) {
this.errorMessage = "Per favore, inserisci un prompt.";
return;
}
this.isLoading = true;
this.analysisResult = null;
this.errorMessage = null;
this.rawAiResponseForDebug = null;
const requestBody = {
contents: [
{
parts: [
{text: this.promptText},
{
inline_data: {
mime_type: this.selectedFile.type,
data: this.imageBase64
}
}
]
}
]
};
const headers = new HttpHeaders({
'Content-Type': 'application/json'
});
this.http.post(this.apiUrl, requestBody, {headers}).subscribe(
(response: any) => {
this.isLoading = false;
try {
const textFromAI = response.candidates?.[0]?.content?.parts?.[0]?.text;
if (!textFromAI) {
if (response.promptFeedback && response.promptFeedback.blockReason) {
this.errorMessage = `Richiesta bloccata dall'AI: ${response.promptFeedback.blockReason}.`;
if (response.promptFeedback.safetyRatings) {
this.errorMessage += ` Dettagli sicurezza: ${JSON.stringify(response.promptFeedback.safetyRatings)}`;
}
console.error('Richiesta bloccata:', response.promptFeedback);
this.rawAiResponseForDebug = JSON.stringify(response.promptFeedback, null, 2);
} else {
throw new Error("Risposta dell'AI non trovata o in formato inatteso nel corpo della risposta.");
}
return;
}
this.rawAiResponseForDebug = textFromAI;
let jsonStringToParse = textFromAI;
const startIndex = jsonStringToParse.indexOf("```json");
const endIndex = jsonStringToParse.lastIndexOf("```");
if (startIndex !== -1) {
const contentStartIndex = startIndex + 7; // Dopo "```json"
if (endIndex > contentStartIndex) {
jsonStringToParse = jsonStringToParse.substring(contentStartIndex, endIndex);
} else {
jsonStringToParse = jsonStringToParse.substring(contentStartIndex);
}
} else if (jsonStringToParse.indexOf("```") !== -1 && jsonStringToParse.trim().startsWith("{") && jsonStringToParse.trim().endsWith("}")) {
console.warn("Trovati ``` ma la stringa trimmata sembra già JSON. Si procede con cautela.");
}
jsonStringToParse = jsonStringToParse.trim();
console.log('Stringa finale da parsare come JSON:', jsonStringToParse);
if (!jsonStringToParse) {
throw new Error("Dopo la pulizia, la stringa JSON risulta vuota.");
}
this.analysisResult = JSON.parse(jsonStringToParse);
this.rawAiResponseForDebug = null;
} catch (error: any) {
this.errorMessage = `Errore nell'interpretare la risposta dell'AI come JSON. Dettagli: ${error.message}. Controlla la console e la risposta grezza qui sotto.`;
}
},
(error: any) => {
this.isLoading = false;
let detailedMessage = '';
if (error.error && error.error.error && error.error.error.message) {
detailedMessage = ` Dettaglio: ${error.error.error.message}`;
}
this.errorMessage = `Errore durante la chiamata API: ${error.status || error.message || 'Errore sconosciuto'}.${detailedMessage} Controlla la console.`;
this.rawAiResponseForDebug = JSON.stringify(error.error, null, 2) || "Nessun dettaglio errore nel corpo della risposta.";
}
);
}
}
Questa la pagina;
<div>
<h2>Analizzatore Immagini con Google AI</h2>
<div>
<label for="prompt">Prompt:</label><br>
<textarea id="prompt" [(ngModel)]="promptText" rows="4" cols="50" style="width: 100%;"></textarea>
</div>
<br>
<div>
<label for="imageUpload">Seleziona un'immagine:</label><br>
<input type="file" id="imageUpload" (change)="onFileSelected($event)" accept="image/png, image/jpeg, image/webp">
</div>
<br>
<button (click)="analyzeImage()" [disabled]="isLoading || !selectedFile">
{{ isLoading ? 'Analisi in corso...' : 'Analizza Immagine' }}
</button>
<div *ngIf="errorMessage" style="color: red; margin-top: 15px; border: 1px solid red; padding: 10px;">
<strong>Errore:</strong> {{ errorMessage }}
<div *ngIf="rawAiResponseForDebug" style="margin-top: 10px;">
<strong>Risposta grezza dall'AI (per debug):</strong>
<pre style="background-color: #ffebeb; color: #333; white-space: pre-wrap; word-wrap: break-word;">{{ rawAiResponseForDebug }}</pre>
</div>
</div>
<div *ngIf="analysisResult && !errorMessage" style="margin-top: 20px;">
<h3>Risultato Analisi:</h3>
<pre>{{ analysisResult | json }}</pre>
</div>
<div *ngIf="imageBase64" style="margin-top: 20px;">
<h4>Anteprima Immagine (selezionata):</h4>
<img [src]="'data:' + (selectedFile?.type || 'image/jpeg') + ';base64,' + imageBase64" alt="Anteprima immagine" style="max-width: 300px; max-height: 300px; border: 1px solid #ccc;">
</div>
</div>
Enjoy!
javascript typescript angular gemini
Commentami!