2015-07-28 10 views
5

Nella mia applicazione Web creata utilizzando laravel 5.1, gli utenti possono caricare alcuni file sensibili che memorizzo in Amazon S3. Più tardi voglio gli utenti CON PERMESSO per scaricare questo file. Dal momento che voglio controllare questo auth, non posso scaricare il file usando i metodi tradizionali dando loro un collegamento diretto al file in S3.Streaming di oggetti Amazon S3 da un server Web utilizzando Laravel

Il mio approccio:

  1. Quando l'utente richiede un download, il mio server di download il file in locale e poi lo streaming per l'utente. Problema: Richiede molto tempo perché i file sono troppo grandi a volte.

  2. Fornire all'utente un URL preconfilato per il download direttamente da S3. L'URL è valido solo per 5 minuti. Problema: Se l'URL è condiviso, chiunque può scaricarlo entro 5 minuti.

  3. According to this article, trasmettere i dati direttamente da S3 ai client. Questo sembra promettente, ma non so come implementarlo.

Secondo questo articolo, ho bisogno di:

  1. Registrati flusso involucro - che è il mio primo problema, perché io non so come ottenere il possesso di S3Client oggetto, perché usi laravel flysystem, e non so quali metodi chiamare per ottenere questo oggetto. Forse dovrei includere il pacchetto S3 separatamente nel mio compositore.json?
  2. Disabilita bufferizzazione dell'uscita - Devo eseguire questa operazione in laravel o laravel ne ha già provveduto?

Sono sicuro che altri sviluppatori hanno già riscontrato un problema simile e vorrebbe ottenere alcuni suggerimenti per l'aiuto. Se qualcuno è già stato trasmesso in streaming direttamente da S3 al client utilizzando laravel Response::download($pathToFile, $name, $headers), mi piacerebbe sentire i tuoi metodi.

+1

Quello che non scorre da S3-> utente, è flussi S3-> Laravel-> utente. Il tuo server è ancora in loop, quindi stai aumentando la larghezza di banda e manchi la maggior parte dei vantaggi offerti da S3. – ceejayoz

+0

@ceejayoz Capisco, ma quale scelta ho. Non posso dare accesso diretto ai file in S3. Inoltre dovrò rendere i file pubblicamente visibili affinché altri possano scaricarli. – Rash

+0

Potrebbe essere possibile associare qualcosa a [credenziali IAM provvisorie] (http://docs.aws.amazon.com/AmazonS3/latest/dev/AuthUsingTempFederationTokenPHP.html) che limita l'uso dell'URL firmato a un particolare IP. – ceejayoz

risposta

8

Dalla discussione nei commenti, sono arrivato ad alcuni punti chiave che vorrei condividere.

Pre-Signed URLs

Come @ceejayoz sottolineato, pre-signed URL non sono una cattiva idea, perché:

  1. posso tenere il tempo a partire da 10 secondi che è perfetto per qualsiasi redirect e di cominciare scarica, ma non abbastanza per il collegamento da condividere.
  2. La mia precedente comprensione era che il download doveva finire nel tempo stabilito. Quindi se il collegamento scade tra 10 secondi, il download deve avvenire prima. Ma @ceejayoz ha sottolineato che non è il caso. Il download avviato ha il permesso di terminare.
  3. Con cloud front, posso anche limitare l'indirizzo IP, per aggiungere più sicurezza.


IAM Roles

Ha anche sottolineato un altro non così grande metodo - per creare utenti IAM temporanei. Questo è un incubo di manutenzione se non eseguito correttamente, quindi fallo solo se sai cosa stai facendo.


Stream From S3

questo è il metodo che ho scelto per ora. Forse più tardi passerò al primo metodo.

Avviso: Se si esegue lo streaming, il server rimane comunque l'intermediario e tutti i dati verranno inviati tramite il server. Quindi se fallisce, o è lento, il download sarà lento.

La mia prima domanda è statahow to register stream wrapper:

Dal momento che sto usando laravel e laravel flysystem utilizza per la gestione S3, non c'era modo semplice per me per ottenere il S3Client. Quindi ho aggiunto pacchetto aggiuntivo AWS SDK for Laravel nel mio composer.json

"aws/aws-sdk-php-laravel" : "~3.0" 

Poi ho scritto il mio codice come segue:

class FileDelivery extends Command implements SelfHandling 
{ 
    private $client; 
    private $remoteFile; 
    private $bucket; 

    public function __construct($remoteFile) 
    { 
     $this->client = AWS::createClient('s3'); 
     $this->client->registerStreamWrapper(); 
     $this->bucket = 'mybucket'; 
     $this->remoteFile = $remoteFile; 
    } 

    public function handle() 
    { 
     try 
     { 
      // First get the meta-data of the object. 
      $headers = $this->client->headObject(array(
       'Bucket' => $this->bucket, 
       'Key' => $this->remoteFile 
      )); 

      $headers = $headers['@metadata']; 
      if($headers['statusCode'] !== 200) 
      { 
       throw new S3Exception(); 
      } 
     } 
     catch(S3Exception $e) 
     { 
      return 404; 
     } 

     // return appropriate headers before the stream starts. 
     http_response_code($headers['statusCode']); 
     header("Last-Modified: {$headers['headers']['last-modified']}"); 
     header("ETag: {$headers['headers']['etag']}"); 
     header("Content-Type: {$headers['headers']['content-type']}"); 
     header("Content-Length: {$headers['headers']['content-length']}"); 
     header("Content-Disposition: attachment; filename=\"{$this->filename}\""); 

     // Since file sizes can be too large, 
     // buffers can suffer because they cannot store huge amounts of data. 
     // Thus we disable buffering before stream starts. 
     // We also flush anything pending in buffer. 
     if(ob_get_level()) 
     { 
      ob_end_flush(); 
     } 
     flush(); 

     // Start the stream. 
     readfile("s3://{$this->bucket}/{$this->remoteFile}"); 
    } 
} 

La mia seconda domanda eraDo I need to Disable output buffering in laravel?

La risposta IMHO è sì. Il buffering consente ai dati di svuotarsi immediatamente dal buffer, con conseguente riduzione del consumo di memoria. Dal momento che non usiamo alcuna funzione di laravel per scaricare i dati sul client, questo non è fatto da laravel e quindi deve essere fatto da noi.