Nozioni di base su AWS

Creazione di un'applicazione Flutter

Creazione di una semplice applicazione Flutter utilizzando AWS Amplify

Modulo 4: aggiunta della possibilità di archiviare immagini

In questo modulo aggiungerai lo storage e la possibilità di associare un'immagine agli appunti nell'app.

Introduzione

Ora che solo gli utenti autenticati possono accedere all'applicazione, possiamo consentire all'utente di fare foto e caricarle in una cartella privata nel bucket di Amazon S3 dell'applicazione.

In questo modulo aggiungeremo la categoria di storage all'applicazione Amplify, caricheremo le immagini acquisite con la fotocamera del dispositivo, quindi scaricheremo e visualizzeremo tutte le foto associate a un singolo utente.

Avrai modo di approfondire i seguenti aspetti

  • Configurazione della categoria di storage
  • Caricamento di file in Amazon S3
  • Download di file da Amazon S3
  • Visualizzazione e acquisizione in cache delle immagini da un URL

Concetti chiave

Storage : il concetto di storage riguarda la possibilità di archiviare file in una posizione e di recuperarli quando occorre. In questo caso, l'archiviazione di immagini in Amazon S3 e il loro download da Amazon S3.

 Tempo richiesto per il completamento

20 minuti

 Servizi utilizzati

Implementazione

  • Creazione del servizio di storage

    Aggiungi il servizio di storage al tuo progetto Amplify eseguendo questo comando nel terminale, nella directory root del progetto:

    amplify add storage

    Come per le altre categorie, nell'interfaccia a riga di comando (CLI) di Amplify ti viene richiesto come desideri configurare il servizio di storage. Useremo il tasto Invio per fornire la risposta predefinita alla maggior parte delle domande:

    ➜  photo_gallery git:(master) amplify add storage
    ? Please select from one of the below mentioned services: Content (Images, audio
    , video, etc.)
    
    ? Please provide a friendly name for your resource that will be used to label th
    is category in the project: s33daafe54
    ? Please provide bucket name: photogalleryf3fb7bda3f5d47939322aa3899275aab
    ? Who should have access: Auth users only

    Quando viene richiesto quale tipo di accesso devono avere gli utenti autenticati, premi il tasto a per selezionare creazione/aggiornamento, lettura ed eliminazione:

    ? What kind of access do you want for Authenticated users? create/update, read,
    delete

    Quindi continua a inserire le risposte predefinite fino a quando la risorsa di storage non risulta completamente configurata:

    ? Do you want to add a Lambda Trigger for your S3 Bucket? No
    Successfully added resource s33daafe54 locally

    Ora, devi inviare la risorsa di storage configurata al nostro back-end in modo da consentire la sincronizzazione. Esegui il seguente comando:

    amplify push

    Nell'interfaccia a riga di comando (CLI) di Amplify verrà fornito un report sullo stato delle modifiche apportate:

    ✔ Successfully pulled backend environment dev from the cloud.
    
    Current Environment: dev
    
    | Category | Resource name        | Operation | Provider plugin   |
    | -------- | -------------------- | --------- | ----------------- |
    | Storage  | s33daafe54           | Create    | awscloudformation |
    | Auth     | photogallery42b5391b | No Change | awscloudformation |
    ? Are you sure you want to continue? (y/n)

    Il report mostra che la categoria di storage è in fase di creazione e l'autenticazione non ha subito alcuna modifica dalla configurazione nel modulo precedente.

    Una volta completata la configurazione della risorsa di storage nel back-end, visualizzeremo un output di corretta esecuzione:

    ✔ All resources are updated in the cloud
  • Installazione della dipendenza

    A questo punto, apri il file pubspec.yaml in Visual Studio Code per aggiungere il plug-in Storage come dipendenza:

    ... # amplify_auth_cognito: '<1.0.0'
    
    amplify_storage_s3: '<1.0.0'
    
    ... # dev_dependencies:

    Ora salva il file in modo che Visual Studio Code installi il plug-in Amplify Auth Cognito. Se la dipendenza non viene installata al momento del salvataggio, puoi anche eseguire $ flutter pub get dal terminale.

    L'output dovrebbe essere questo:

    exit code 0
  • Configurazione del plug-in

    Ora torna in main.dart in modo che Storage possa essere aggiunto come plug-in nella nostra istanza di Amplify:

    ... // void _configureAmplify() async {
    
    _amplify.addPlugin(
        authPlugins: [AmplifyAuthCognito()],
        storagePlugins: [AmplifyStorageS3()]);
    
    ... // try {

    Esegui l'app. Nei log dovresti visualizzare il messaggio di corretta esecuzione, che indica che Amplify è ancora configurato correttamente e include il plug-in Storage.

    flutter: Successfully configured Amplify 🎉
  • Implementazione della funzionalità

    Per organizzare il codice, creiamo un file storage_service.dart separato che includa la logica per l'upload e il download dei file. Aggiungi il seguente codice:

    import 'dart:async';
    import 'package:amplify_core/amplify_core.dart';
    import 'package:amplify_storage_s3/amplify_storage_s3.dart';
    
    class StorageService {
      // 1
      final imageUrlsController = StreamController<List<String>>();
    
      // 2
      void getImages() async {
        try {
          // 3
          final listOptions =
              S3ListOptions(accessLevel: StorageAccessLevel.private);
          // 4
          final result = await Amplify.Storage.list(options: listOptions);
    
          // 5
          final getUrlOptions =
              GetUrlOptions(accessLevel: StorageAccessLevel.private);
          // 6
          final List<String> imageUrls =
              await Future.wait(result.items.map((item) async {
            final urlResult =
                await Amplify.Storage.getUrl(key: item.key, options: getUrlOptions);
            return urlResult.url;
          }));
    
          // 7
          imageUrlsController.add(imageUrls);
        
        // 8
        } catch (e) {
          print('Storage List error - $e');
        }
      }
    }
    1. Partiamo inizializzando uno StreamController che gestisca gli URL di immagini richiamati da Amazon S3.
    2. Questa funzione avvia il processo di recupero delle immagini che devono essere visualizzate in GalleryPage.
    3. Poiché vogliamo mostrare solo le foto che l'utente ha caricato, specifichiamo il livello di accesso StorageAccessLevel.private, assicurandoci che le foto private dei nostri utenti restino private.
    4. Successivamente, chiediamo a Storage di elencare tutte le foto pertinenti in base alle S3ListOptions.
    5. Se il risultato dell'elenco è corretto, dobbiamo procurarci l'URL di download effettivo di ogni foto, in quanto il risultato dell'elenco contiene solo un elenco di chiavi e non l'URL effettivo delle foto.
    6. Utilizziamo il file .map per interagire su ciascun elemento dell'elenco e restituire in modo asincrono l'URL di download di ciascun elemento.
    7. Infine, inviamo l'elenco degli URL da osservare.
    8. Se si verificano errori, stampiamo l'errore.

    In iOS, per garantire che l'applicazione possa scaricare le immagini, dobbiamo aggiornare la funzione App Transport Security (Sicurezza per il trasferimento delle app) in Info.plist (ios > Runner > Info.plist):

    ... <!-- <string>Need to take pictures</string> -->
    
    <key>NSAppTransportSecurity</key>
    <dict>
       <key>NSAllowsArbitraryLoads</key>
       <true/>
    </dict>
    
    ... <!-- </dict> -->

    Se non hai caricato nulla, è inutile tentare di elencare gli URL di immagini da S3, quindi aggiungiamo una funzione per caricare le immagini:

    // 1
    void uploadImageAtPath(String imagePath) async {
     final imageFile = File(imagePath);
     // 2
     final imageKey = '${DateTime.now().millisecondsSinceEpoch}.jpg';
    
     try {
       // 3
       final options = S3UploadFileOptions(
           accessLevel: StorageAccessLevel.private);
    
       // 4
       await Amplify.Storage.uploadFile(
           local: imageFile, key: imageKey, options: options);
    
       // 5
       getImages();
     } catch (e) {
       print('upload error - $e');
     }
    }
    1. Si tratta di una funzione asincrona che acquisisce un percorso di immagine fornito dalla fotocamera.
    2. Per garantire che la foto abbia una chiave univoca, useremo un timestamp come chiave.
    3. Come indicato durante l'implementazione di getImages, dobbiamo specificare il livello di accesso StorageAccessLevel.private, in modo che l'utente carichi le immagini presenti nella propria cartella nel bucket S3.
    4. Carichiamo il file specificando la relativa chiave, quindi carichiamo le opzioni del file.
      Infine, chiamiamo getImages per ottenere l'ultimo elenco di URL di immagini e inviarle.
    5. Abbiamo completato tutta la codifica necessaria per eseguire correttamente upload e download. Ora dobbiamo collegare ogni cosa e testarla.

    Iniziamo aggiornando GalleryPage per utilizzare uno StreamController come argomento, in modo che possa esaminare gli URL di immagini recuperati da Storage.

    ... // class GalleryPage extends StatelessWidget {
    
    final StreamController<List<String>> imageUrlsController;
    
    ... // final VoidCallback shouldLogOut;
    ... // final VoidCallback shouldShowCamera;
    
    GalleryPage(
       {Key key,
       this.imageUrlsController,
       this.shouldLogOut,
       this.shouldShowCamera})
        : super(key: key);
    
    ... // @override

    Quindi, aggiorniamo _galleryGrid in modo che restituisca uno StreamBuilder e non solo GridView.builder:

    Widget _galleryGrid() {
     return StreamBuilder(
         // 1
         stream: imageUrlsController.stream,
         builder: (context, snapshot) {
           // 2
           if (snapshot.hasData) {
             // 3
             if (snapshot.data.length != 0) {
               return GridView.builder(
                   gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                       crossAxisCount: 2),
                   // 4
                   itemCount: snapshot.data.length,
                   itemBuilder: (context, index) {
                     return CachedNetworkImage(
                       imageUrl: snapshot.data[index],
                       fit: BoxFit.cover,
                       placeholder: (context, url) => Container(
                           alignment: Alignment.center,
                           child: CircularProgressIndicator()),
                     );
                   });
             } else {
               // 5
               return Center(child: Text('No images to display.'));
             }
           } else {
             // 6
             return Center(child: CircularProgressIndicator());
           }
         });
    }
    1. Lo StreamBuilder utilizzerà l'imageUrlsController trasmesso da StorageService per fornire le istantanee dei nostri dati.
    2. L'interfaccia utente richiede che l'istantanea contenga i dati per visualizzare tutto ciò che è rilevante per l'utente.
    3. Dobbiamo anche determinare se i dati contengono effettivamente degli elementi. In questo caso, continuiamo a creare la GridView.
    4. Invece di utilizzare un numero codificato, possiamo ora stabilire la dimensione della GridView in base alla lunghezza dei dati presenti nella nostra istantanea.
    5. Se l'istantanea non contiene elementi, in un testo visualizzato viene indicato che non ci sono elementi da mostrare.

    Al momento, stiamo ancora mostrando un segnaposto per ogni elemento della griglia. Dobbiamo scaricare ciascuna immagine dall'URL fornito dal flusso. Per semplificare questa operazione, aggiungiamo una nuova dipendenza in pubspec.yaml:

    ... # amplify_storage_s3: '<1.0.0'
    
    cached_network_image: ^2.3.3
    
    ... # dev_dependencies:

    Questa libreria fornisce un widget che consente di scaricare e inserire nella cache un'immagine semplicemente ricevendo un URL. Salva le modifiche e torna a GalleryPage in modo da poter sostituire il segnaposto:

    ... // itemBuilder: (context, index) {
    
    return CachedNetworkImage(
      imageUrl: snapshot.data[index],
      fit: BoxFit.cover,
      placeholder: (context, url) => Container(
          alignment: Alignment.center,
          child: CircularProgressIndicator()),
    );
    
    ... // itemBuilder closing });

    Il segnaposto è stato sostituito con CachedNetworkImage che viene passato nell'URL dall'istantanea e indicizzato attraverso itemBuilder. Mentre l'immagine viene caricata, il widget visualizza un CircularProgressIndicator.

    Ora possiamo collegare GalleryPage e CameraPage a StorageService in _CameraFlowState. Inizia creando una proprietà per contenere un'istanza di StorageService:

    ... // bool _shouldShowCamera = false;
    
    StorageService _storageService;
    
    ... // List<MaterialPage> get _pages {

    Quindi, inizializza _storageService nel metodo initState:

    ... // _getCamera();
    
    _storageService = StorageService();
    _storageService.getImages();
    
    ... // initState closing }

    Subito dopo aver inizializzato StorageService, chiamiamo getImages in modo da recuperare tutte le immagini caricate.

    Passiamo ora lo StreamController alla GalleryPage:

    ... // child: GalleryPage(
        
    imageUrlsController: _storageService.imageUrlsController,
    
    ... // shouldLogOut: widget.shouldLogOut,

    Infine, aggiorniamo la funzionalità di didProvideImagePath della CameraPage una volta scattata la foto:

    ... // this._toggleCameraOpen(false);
    
    this._storageService.uploadImageAtPath(imagePath);
    
    ... // didProvideImagePath closing }

    È tutto! Possiamo iniziare a scattare foto con la nostra applicazione e caricarle in S3.

    Crea l'applicazione e inizia a provare.

    EndofModule4-gif

Conclusione

Complimenti! È stata implementata la funzionalità principale dell'applicazione: l'utente può fare foto che verranno archiviate in una sezione privata del bucket di Amazon S3 dell'applicazione.

Nel prossimo modulo, aggiungeremo gli strumenti di analisi.

Questo modulo è stato utile?

Grazie
Facci sapere cosa ti è piaciuto.
Chiudi
Spiacenti di non esserti stati d'aiuto
C'è qualcosa di obsoleto, ambiguo o approssimativo? Aiutaci a migliorare questo tutorial con il tuo feedback.
Chiudi

Aggiungi strumenti di analisi