Come gestire file in modo sicuro con Angular e Amazon S3, senza dover scrivere API

Amazon Web Services offre un ottimo e conosciutissimo servizio per lo storage di file chiamato S3 (Simple Storage Service) che in Innoteam apprezziamo particolarmente per la sua semplicità, robustezza e per il suo pricing particolarmente conveniente.

In questo articolo vedremo come interfacciare una Web App con il servizio S3 di Amazon per effettuare upload e download di file in maniera sicura ed efficiente.

Il punto di forza dell’approccio descritto è che upload e download vengono effettuati direttamente verso il bucket S3 senza necessità di dover appoggiare i file su server intermediari.

Supponiamo di dover sviluppare un’app gestionale che ci permetta di caricare e scaricare documenti.

Bucket S3

Per questo tipo di applicazioni non possiamo utilizzare un bucket pubblico e accessibile da chiunque ma, anzi, vogliamo creare un bucket privato a cui possa accedere solo e soltanto il nostro applicativo.

Creiamo quindi un bucket privato dal pannello di amministrazione di Amazon S3 come indicato nella guida ufficiale di AWS.

Pre-Signed URL

Come possiamo gestire upload e download di file verso un bucket privato?
Utilizziamo una feature di Amazon S3 chiamata Pre-Signed URL.

Una Pre-Signed URL è una particolare URL che è stata generata e “firmata” dall’SDK di Amazon e che ci permette di accedere ad un ben definito oggetto di un bucket S3 in lettura oppure in scrittura in base al tipo di Pre-Signed URL.

Chi è in possesso di una di queste URL è quindi in grado fare il download oppure l’upload di uno specifico file, effettuando verso questa URL rispettivamente una chiamata di tipo HTTP GET per il download e HTTP PUT per l’upload.
Una Pre-Signed URL ha una validità temporale prefissata, terminata la quale non è più utilizzabile.

Per poter utilizzare le Pre-Signed URL è necessario modificare le configurazioni CORS del bucket S3 che abbiamo creato, in modo che vengano accettate chiamate HTTP di tipo GET e PUT verso il bucket.

E’ possibile modificare la configurazione CORS del bucket dal pannello di amministrazione di Amazon S3:
Cliccare sul nome del bucket -> Permissions -> CORS configuration

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>PUT</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
  </CORSRule>
  <CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>Authorization</AllowedHeader>
  </CORSRule>
</CORSConfiguration>

Architettura

In questo articolo faremo riferimento ad un’architettura che prevede un’applicazione Angular ed un’API REST JSON con un DB relazionale sottostante.

Modello Dati

Nella nostra applicazione terremo traccia di tutti i file presenti sul bucket S3, creando un record nel nostro DB associato ad ogni file.

Avremo quindi una tabella `attachments` strutturata in questo modo:

Campo Descrizione
filename: string Nome del file (key nel bucket)
uploaded: boolean Flag che indica se l’upload del file sul bucket è terminato con successo

E avremo i seguenti endpoint di una classica API REST:

Endpoint Descrizione
POST /attachments Creazione di un nuovo record di tipo Attachment
PUT /attachments/:id Modifica di un record esistente di tipo Attachment
GET /attachments/:id Recupero di un record esistente di tipo Attachment

Download di un file

Quando il frontend Angular effettua una richiesta GET all’endpoint `/attachments/:id` per il recupero di un record di tipo Attachment, la nostra API, nel caso il record abbia flag `uploaded` impostato a `true`, provvederà a generare una Pre-Signed URL di lettura e ad aggiungerla alle informazioni prelevate da DB.

Generazione di una Pre-Signed URL di lettura in PHP:

// Creating a presigned URL
$cmd = $s3Client->getCommand('GetObject', [ 'Bucket' => 'my-bucket', 'Key' => 'testKey' ]);
$request = $s3Client->createPresignedRequest($cmd, '+20 minutes');

// Get the actual presigned-url
$presignedUrl = (string) $request->getUri();

Body della risposta:

{
  filename: "notes.txt",
  uploaded: true,
  presignedUrl: "https://..."
}

Upload di un file

L’upload di un file verrà invece effettuato in tre passaggi.

1. Creazione di un record Attachment in DB e conseguente recupero della Pre-Signed URL di scrittura

Il frontend Angular effettua una richiesta POST all’endpoint `/attachments` per la creazione di un nuovo record di tipo Attachment.

Body della richiesta:

{ filename: "notes.txt" }

L’API REST crea un record nel DB, imposta automaticamente il flag `uploaded` di questo record a `false` e utilizza l’SDK di AWS per generare una Pre-Signed URL di scrittura.
La Pre-Signed URL viene restituita ad Angular.

Generazione di una Pre-Signed URL di scrittura in PHP:

// Creating a presigned URL
$cmd = $s3Client->getCommand('PutObject', [ 'Bucket' => 'my-bucket', 'Key' => 'testKey' ]);
$request = $s3Client->createPresignedRequest($cmd, '+20 minutes');

// Get the actual presigned-url
$presignedUrl = (string) $request->getUri();

Body della risposta:

{
  id: 44,
  filename: "notes.txt",
  presignedUrl: "https://...",
  uploaded: false
}

2. Upload vero e proprio direttamente verso il bucket di Amazon S3

Angular farà semplicemente una richiesta HTTP PUT verso la Pre-Signed URL ricevuta dalle API con il contenuto del file.

Upload di file utilizzando Pre-Signed URL in service Angular:

@Injectable()
export class AttachmentService {
  ...
  constructor(private http: HttpClient) {}
  ...
  upload(file: File, presignedUrl: string): Promise<any> {
    const options = {
      headers: { 'Content-Type': file.type },
      responseType: 'text'
    };
    return this.http
      .put(presignedUrl, file, options)
      .toPromise();
  }
  ...
}

3. Aggiornamento del record precedentemente creato con flag `uploaded` impostato a `true`

Una volta terminato l’upload verso il bucket S3, Angular chiamerà le API per aggiornare il record Attachment nel DB.

Body della richiesta:

{ uploaded: true }

I 3 step possono essere riassunti dal seguente diagramma:

Conclusioni

Uno dei più grandi vantaggi di questo approccio è sicuramente il fatto di poter effettuare upload e download comunicando direttamente con il bucket S3 senza dover passare dal server che ospita le API, sgravandolo dal lavoro di intermediario verso S3 e non occupandone inutilmente banda e risorse.

Ci teniamo inoltre a sottolineare che per gli esempi è stato utilizzato codice PHP e Angular ma l’approccio descritto è del tutto generale e può essere applicato con qualsiasi combinazione di tecnologie Backend/Frontend.

Testare angular

Eseguire test singoli tramite Karma ed Angular CLI quando i test rallentano lo sviluppo dei progetti più grandi

Le modifiche da applicare ad un progetto standard per avere più controllo sull’esecuzione dei test unitari tramite karma.

Per chi sviluppa frontend, le questioni relative al testing sono spesso “meno lineari” rispetto al classico approccio TDD dello sviluppo backend. Chi ha avuto occasione di lavorare in angular 2 avrà sicuramente potuto apprezzare angular CLI e gli strumenti che mette a disposizione, soprattutto per il testing.

Nello sviluppo quotidiano ho riscontrato che “ng test”, il comando che ci permette di eseguire test unitari tramite karma, non fornisce la possibilità di scegliere quali test eseguire, cosa che al crescere del numero dei test rallenta considerevolmente l’esecuzione, soprattutto in modalità “watch” rendendo così quasi impossibile un approccio TDD.

Infatti, ci capita spesso di lavorare su progetti frontend molto complessi per i nostri clienti e, nonostante questo tipo di problema, al contempo vogliamo mantenere alti i livelli qualitativi, anche con tecniche come appunto il TDD.

Per ovviare al problema ho perciò applicato le seguenti modifiche al mio progetto angular per avere la possibilità di lanciare e tenere in modalità watch solo i test che mi interessano durante lo sviluppo.

Continua a leggere

La pubblicazione di contenuti con eZ Publish su siti ad alto traffico

Se dovete realizzare nuove soluzioni digitali ad alto traffico o comunque critiche per il business, assicuratevi di utilizzare alla base tecnologie che consentano di essere configurate e sviluppate appropriatamente per scalare quando necessario.

Non fatevi intrappolare da strumenti che seppur molto noti e diffusi pongono seri limiti alla crescita e comportano tempi e costi elevati per essere adattati.

Ad esempio, il CMS eZ Publish contiene funzionalità avanzate che possono essere attivate in tempi rapidi come la pubblicazione asincrona.

E’ vero che sono richiesti degli specialisti per governare simili tecnologie ma i benefici per il business sono molto elevati.

Continua a leggere

Disegno architetturale della Innomatic Platform

Questo è il disegno architetturale della Innomatic Platform:

 

Catalogo Magento – Concetti Base

Ora che avete installato con successo la versione desiderata di Magento Community, possiamo tranquillamente iniziare a scoprire la più importante delle funzionalità che ci offre questo potente strumento di e-commerce, il Catalogo.

L’organizzazione del catalogo è alla base di un sistema che prevede stabilità, gestione semplice delle categorie e dei prodotti, ma soprattutto velocità di navigazione sul frontend.

Quest’ultima è la peculiarità base di ogni e-commerce che si rispetti, quindi l’importanza di mantenere il sistema Magento il più pulito possibile ci dà la possibilità di consultare il forum di Magento in caso di problemi, ma soprattutto siamo liberi di condividere il problema con un ampio numero di utenti, pronti a darci supporto. Chiaramente, più il nostro sistema è vicino a quello standard, meno difficoltà avremo nel condividere tale problema con la community di Magento.

Continua a leggere