PROGETTARE LA FUNZIONALITA’ DI MODIFICA

SALVARE UN FILE CARICATO DALL’UTENTE

NET CORE

Vedremo come sia possibile salvare un’ immagine, in modo che sia fruibile a tutti gli utenti dell’applicazione. Per salvare l’immagine ho creato una nuova interfaccia nel servizio infrastrutturale IImagePersister.cs. Ti mostro il codice.

Interfaccia

Restituisce un Task<string> perché questo servizio infrastrutturale sa in quale posizione persistere l’immagine e come raggiungerla. La classe che implementa l’interfaccia si chiama InsecureImagePersister.cs, e tra poco capirai il perché sia insicura. Il metodo utilizzerà uno Stream di dati, vediamo cosa si intende. A prescindere da dove si trovino i file binari dell’immagine, in RAM, su un file temporaneo su disco, su un Web Services il bello dello Stream è che offre un’interfaccia per accedere al file in maniera sequenziale. CopyToAsync metodo di IFormFile copierà il file binario dallo stream di input allo stream di output che decideremo noi quale sia.

Stream

Il metodo copia i file tali e quali senza nessuna verifica; quindi, anche se l’utente caricasse un virus questo virus verrebbe trasferito su disco, da qui si capisce perché è insicura questa classe. Questo è il codice.

Implementazione

RIDIMENSIONARE UN’IMMAGINE CARICATA DALL’UTENTE

A volte può capitare che l’immagine nel Form di dettaglio non si aggiorni. Questo perché non essendo cambiato il path all’immagine ma solo sovrascritta, il Browser pensa che può servire l’immagine dalla sua cache locale anziché leggerla nuovamente dal server. Vediamo come risolvere.

asp-append-version

Come vedi è stato aggiunto un codice Hash dell’immagine facendo cambiare il path e forzando il Browser a caricare la nuova immagine. Occupiamoci ora di una questione molto importante. La validazione dell’immagine, effettuata tramite una libreria esterna scaricata da NuGet.

Libreria

Con la libreria che scarichiamo da NuGet possiamo fare validazione e ritagliare l’immagine, tuttavia, non ci mette al sicuro da tutti i possibili abusi come il caricare un’immagine pornografica. In questo caso si potrebbe caricare l’immagine e con l’oggetto File spostarla su un altro percorso affinché possa essere vagliata dal personale amministratore. Oppure utilizzando un Web Service basato su IA. La libreria che andremo a scaricare da NuGet è Magick.NET-Q8-AnyCPU. Andiamo a creare una nuova classe nel servizio infrastrutturale con questa libreria e la chiamiamo MagickNetImagePersister.cs. Nella classe facciamo uso del metodo OpenReadStream di IFormFile. Vediamo un’immagine.

Magick net

Vediamo vari tipi di ridimensionamento. Se l’immagine originale ha le stesse proporzioni di quella che vogliamo ottenere dobbiamo semplicemente ridimensionarla con i valori indicati nel codice, 300px in larghezza e 300px in altezza.

Ridimensionamento

Vediamo il caso in cui l’immagine non è quadrata, e quindi non si possono mantenere le sue proporzioni. Ci sono varie strategie, la prima non consigliabile è quella di far occupare all’immagine tutta la superficie.

Ridimensionamento

La seconda strategia consente di lasciare inalterate le proporzioni, ma lasciando ai lati delle bande bianche.

Ridimensionamento

La terza strategia consente di mantenere le proporzioni tagliando le bande superiori e inferiori. In questi casi si può informare l’utente dicendo non hai caricato un’immagine quadrata quindi potrebbe essere ritagliata. Vediamo come si comporta la nostra libreria.

Ridimensionamento
Ridimensionamento
Ridimensionamento

Il secondo parametro MagickColor.FromRgb indica il colore delle bande laterali, bianche in questo caso. L’immagine sotto riportata illustra la strategia che utilizzeremo.

Ridimensionamento

MOSTRARE IN ANTEPRIMA IL RISULTATO DEL RIDIMENSIONAMENTO

Su questo argomento non c’è molto da dire in quanto si devono creare solo delle regole CSS.

Anteprima

PROTEGGERSI DA ABUSI LEGATI AL CARICAMENTO DELLE IMMAGINI

Quando una libreria come Magick.Net ingerisce i byte dell’immagine deve elaborarli in qualche modo perché quelli sono i byte di un formato compresso come jpeg o png. Formato compresso significa che i byte riescono a rappresentare una elevata quantità di pixel, quindi Magick.Net per poter lavorare deve prima decomprimere l’immagine al suo formato originale. Decomprimerla significa che ogni singolo pixel deve essere rappresentato nella memoria RAM. Questo significa che l’occupazione della memoria RAM è proporzionale al numero di pixel con cui è formata l’immagine. Facciamo un esempio. Supponiamo che l’utente carichi un’immagine di 4000px per 3000px, essendo i canali 3 verrebbe ad occupare in RAM circa 36MB.

Decompressione

Ma se l’utente carica un’immagine di 40000px per 30000px l’occupazione diventa 36GB. Il numero di pixel con cui è formata l’immagine si può facilmente impostare nel costruttore della classe Magick.NET. Al superamento di tale limite la libreria genera un’eccezione. Ma se l’utente invia molte richieste? Come facciamo a proteggere l’applicazione? In questo caso possiamo usare un oggetto del .NET Core, il SemaphoreSlim.

Limitare il consumo di ram

Con l’ultima istruzione è come se stessimo predisponendo una strada a due corsie, in cui il passaggio è regolato da un semaforo. Le macchine (I Thread di ASP.NET Core che vanno ad eseguire il codice del ridimensionamento dell’immagine) sono costrette a passare una alla volta, quando sono arrivate al traguardo (fine elaborazione) il semaforo diventa nuovamente verde. Quindi con tale meccanismo riusciamo a limitare il numero di Thread a due.

SemaphoreSlim
Limitare la ram

Un Thread che arriva ad eseguire questo codice trovando l’operatore await si mette in attesa su una delle due corsie che abbiamo a disposizione, se era già verde tanto di guadagnato vuol dire che WaitAsync() si completerà subito e il Thread inizierà la sua elaborazione. È importante dopo l’istruzione await mettere un blocco try finally, questo perché se si dovesse verificare un’eccezione il blocco finally comunque verrebbe eseguito e la luce del semaforo tornerà verde. Altrimenti si formerebbero code di Thread e la nostra applicazione smetterà di funzionare. Se non invocassimo il metodo Release la luce resterebbe sempre accesa e si formerebbero dei Thread in attesa. Anzichè far elaborare il tutto alla libreria per poi generare un’eccezione si può impostare un limite nel file di configurazione, in tal caso è il Web Server Kestrel che bloccherà subito l’esecuzione.

Limitare il peso delle immagini

LINK AL CODICE SU GITHUB

GITHUB

Scaricare il codice della sezione15 oppure il ramo master oppure clonare il repository GITHUB per avere a disposizione tutte le sezioni nel tuo editor preferito.