[PYTHON] Erstellt einen Mechanismus zum Herunterladen einer großen Zip-Datei mit Basisauthentifizierung mit aws

Artikelinhalt in drei Zeilen

Überblick

Wenn beim Teilen einer großen Datei die E-Mail mehr als 8 MB überschreitet, wird sie blockiert, da es sich um einen Mailserver handelt. Selbst wenn es nicht blockiert ist, kann das Senden einer großen Datei per E-Mail ziemlich ärgerlich sein, z. B. das Laden des Servers. Ich möchte den Speicherdienst nutzen, aber wenn es sich um einen Giga-Dateiflug handelt, ärgere ich mich wahrscheinlich über die Sicherheitsvereinbarung (ich habe sie in der Vergangenheit häufig verwendet), und Google Drive ist besorgt über die Freigabe ohne Google-Konto (Zugriff, wenn ich die URL nicht kenne). Obwohl dies nicht möglich ist, ist es für alle schwierig, darauf zuzugreifen.

Ich möchte nur eine URL, über die ich vorübergehend eine ID anwenden und eine große Datei freigeben und freigeben kann ... (Löschen Sie sie nach dem Ende oder machen Sie sie nach Ablauf des Zeitlimits privat.)

Daher habe ich kürzlich SAA von aws erhalten und mich herausgefordert, einen Mechanismus zum einfachen Teilen von Dateien mit aws als Überprüfung zu erstellen. Dies war der Grund für diesen Artikel.

Persönlich wollte ich den Server nicht ein- oder ausschalten oder überwachen, also fragte ich mich, ob ich ihn ohne Server erstellen könnte.

Die Größe der Datei, die Sie freigeben möchten, beträgt übrigens etwa 100 MB.

Arbeiten Sie mit API Gateway, Lambda und S3 zusammen

Jede Rolle jedes Dienstes

API Gateway

So veröffentlichen Sie Lambda und wenden die Standardauthentifizierung auf die veröffentlichte URL an. Nach der Überprüfung verfügt API Gateway über eine Methode namens "API Gateway Lambda Authorizer", mit der Sie sich mit einem Zugriffsschlüssel authentifizieren und Lambda vorbereiten und den API-Zugriff damit steuern können.

Lambda

Bereiten Sie eine Lambda-Funktion vor, die die Basisauthentifizierung für die Verwendung des "API Gateway Lambda Authorizer" anwendet, und bereiten Sie zwei Seiten für das Herunterladen der Zip-Datei nach der Basisauthentifizierung mit der Lambda-Funktion vor.

Eine besteht darin, HTML anzuzeigen und sie zu bitten, die herunterzuladende Datei auszuwählen. Zuerst dachte ich, es wäre in Ordnung, es herunterzuladen, wenn ich auf die URL trete, aber was ist, wenn es mehrere Dateien gibt? Also habe ich beschlossen, eine HTML-Seite vorzubereiten. (Wenn ich jetzt darüber nachdenke, bin ich froh, dass ich den Abfrageparameter verwendet habe. Ja ... → Ich dachte, aber es hat nicht funktioniert. Siehe unten.)

Die andere ist eine Lambda-Funktion, die Dateien aus S3 herunterlädt und eine Zip-Datei (binär) zurückgibt.

S3

Verwenden Sie es als Speicherort für Zip-Dateien, die Sie herunterladen möchten, und als Speicherort für HTML-Vorlagen.

Diagramm

TODO: Erstellen Sie ein Diagramm und fügen Sie es ein

Implementierung / Einstellung

Lambda

Funktionsname: download ___ auth

Lambda für die Basisauthentifizierung

import json
import os
import base64


def lambda_handler(event, context):
	policy = {
        'principalId': 'user',
        'policyDocument': {
          'Version': '2012-10-17',
          'Statement': [
            {
              'Action': 'execute-api:Invoke',
              'Effect': 'Deny',
              'Resource': event['methodArn']
            }
          ]
        }
    }

    if not basic_auth(event):
        print('Auth Error!!!')
        return policy
        
    policy['policyDocument']['Statement'][0]['Effect'] = 'Allow'
    return policy
    

def basic_auth(event):
    if 'headers' in event.keys() and 'authorization' in event['headers'].keys():
        auth_header = event['headers']['authorization']
    
    	#Informationen aus Lambda-Umgebungsvariablen abrufen.
        user = os.environ['USER']
        password = os.environ['PASSWORD']
        print(os.environ)
        
        _b64 = base64.b64encode('{}:{}'.format(user, password).encode('utf-8'))
        auth_str = 'Basic {}'.format(_b64.decode('utf-8'))
        return auth_header == auth_str

    raise Exception('Auth Error!!!')

Funktionsname: download ___ index

Ein Lambda, das herunterladbare Dateien in HTML anzeigt. Die HTML-Vorlage wird von S3 bezogen. Die Template-Engine ist Jinja.

from jinja2 import Template
import boto3
from botocore.exceptions import ClientError

import os
import logging


logger = logging.getLogger()
S3 = boto3.resource('s3')
TEMPLATE_AWS_S3_BUCKET_NAME = 'hogehoge-downloader'
BUCKET = S3.Bucket(TEMPLATE_AWS_S3_BUCKET_NAME)


def get_object(bucket, object_name):
    """Retrieve an object from an Amazon S3 bucket

    :param bucket_name: string
    :param object_name: string
    :return: botocore.response.StreamingBody object. If error, return None.
    """
    try:
        response = bucket.Object(object_name).get()
    except ClientError as e:
        # AllAccessDisabled error == bucket or object not found
        logging.error(e)
        return None
    # Return an open StreamingBody object
    return response['Body'].read()



def main():
    index_html = get_object(BUCKET,
                            os.path.join('template', 'index.html')) \
                            .decode('utf8')
    li_html = get_object(BUCKET,
                          os.path.join('template', 'file_li.html')) \
                          .decode('utf8')

    index_t = Template(index_html)
    insert_list = []
    objs = BUCKET.meta.client.list_objects_v2(Bucket=BUCKET.name,
                                              Prefix='files')
    for obj in objs.get('Contents'):
        k = obj.get('Key')
        ks = k.split('/')
        if ks[1] == '':
            continue

        file_name = ks[1]
        print(obj.get('Key'))
        li_t = Template(li_html)
        insert_list.append(li_t.render(
            file_url='#',
            file_name=file_name
        ))

    output_html = index_t.render(file_li=''.join(insert_list))
    return output_html


def lambda_handler(event, context):
    output_html = main()
    
    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": 'text/html'
        },
        "isBase64Encoded": False,
        "body": output_html
    }

Funktionsname: download ___ download

Datei von s3 lambda herunterladen

import boto3
from botocore.exceptions import ClientError

import os
import logging
import base64

logger = logging.getLogger()
S3 = boto3.resource('s3')
TEMPLATE_AWS_S3_BUCKET_NAME = 'hogehoge-downloader'
TEMPLATE_BUCKET = S3.Bucket(TEMPLATE_AWS_S3_BUCKET_NAME)


def get_object(bucket, object_name):
    """Retrieve an object from an Amazon S3 bucket

    :param bucket_name: string
    :param object_name: string
    :return: botocore.response.StreamingBody object. If error, return None.
    """
    try:
        response = bucket.Object(object_name).get()
    except ClientError as e:
        # AllAccessDisabled error == bucket or object not found
        logging.error(e)
        return None
    # Return an open StreamingBody object
    return response['Body'].read()


def lambda_handler(event, context):
    file_name = event['queryStringParameters']['fileName']
    body = get_object(TEMPLATE_BUCKET, os.path.join('files', file_name))
    return {
        "statusCode": 200,
        "headers": {
            "Content-Disposition": 'attachment;filename="{}"'.format(file_name),
            "Content-Type": 'application/zip'
        },
        "isBase64Encoded": True,
        "body": base64.b64encode(body).decode('utf-8')
    }

API Gateway

Erstbau

--Wählen Sie REST-API als API-Typ

So gebaut.

image.png

Authorizer-Einstellungen

Stellen Sie ein, dass Sie mit der Lambda-Funktion arbeiten, die die Standardauthentifizierung durchführt.

image.png

Stellen Sie wie folgt ein. Deaktivieren Sie den Autorisierungscache. (Wenn Sie es aktiviert lassen und mehrere Ressourcen festlegen, verhält es sich seltsam.)

スクリーンショット 2020-03-15 11.41.06.png

Damit sind die Authentifizierungseinstellungen abgeschlossen. Der Rest ist in Ordnung, wenn Sie ihn mit jeder Methode festlegen.

Erstellen einer Ressourcenmethode

Bereiten Sie eine Ressourcenmethode wie unten gezeigt vor.

image.png

Die detaillierten Einstellungen für jede werden unten erklärt.

/html

Aktivieren Sie die Option Lambda-Proxy-Integration verwenden. Durch Einfügen können Header usw. auf der Lambda-Seite definiert werden.

image.png

Legen Sie die Methodenanforderung für die Authentifizierung mit der Standardauthentifizierung fest.

image.png

Geändert, damit HTML in der Methodenantwort erkannt werden kann. Ändern Sie den Inhaltstyp des Antwortkörpers in "text / html".

image.png

Damit ist die Einstellung abgeschlossen.

/download

Aktivieren Sie wie bei der HTML-Seite die Option Lambda-Proxy-Integration verwenden.

image.png

Auch hier wird die Standardauthentifizierung festgelegt.

Da ich den Dateinamen erhalten möchte, der als Parameter heruntergeladen werden soll, habe ich im Parameter URL-Abfragezeichenfolge einen Parameter mit dem Namen "Dateiname" festgelegt. (Wenn Sie es obligatorisch machen möchten, überprüfen Sie es)

image.png

Der Inhaltstyp der Methodenantwort wurde geändert, da ich die Zip-Datei herunterladen möchte.

image.png

Fügen Sie abschließend den binären Medientyp aus den Einstellungen im linken Menü hinzu. Wenn Sie hier den Zielinhaltstyp festlegen, wird der Rückgabewert (base64) von Lambda in Binär konvertiert. (Sie müssen jedoch den Inhaltstyp zum Anforderungsheader oder "application / zip" hinzufügen, um zu akzeptieren.)

image.png

S3

Bucket-Name: Erstellen Sie "hogehoge-downloader" und erstellen Sie das folgende Verzeichnis.

--files: Speichern Sie Zip-Dateien --template: Bereiten Sie eine HTML-Vorlagendatei vor

Die HTML-Vorlage ist unten.

index.html

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>Downloader</title>
      <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css">
      <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
   </head>
   <body>
      <section class="section">
         <div class="container">
            <div class="container">
               <ul>{{ file_li }}</ul>
         </div>
         </div>
      </section>
   </body>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script>
      function download(e) {
        const zipUrl = 'https://xxx.aws.com/prod/download?fileName=' + this.filename;
        const blob = axios.get(zipUrl, {
            responseType: 'blob',
            headers: {
                Accept: 'application/zip'
            },
        }).then(response => {
            window.URL = window.URL || window.webkitURL;
            const uri = window.URL.createObjectURL(response.data);
            const link = document.createElement('a');
            link.download = this.filename;
            link.href = uri;
            link.click()
        }).catch(error => {
            console.log(error);
        });
    }

    var links = Array.from(document.getElementsByClassName('downloadLink'));
    links.map(l => l.addEventListener('click', {filename: l.dataset.filename, handleEvent: download}));
  </script>
</html>

file_li.html

<li>
	<a href="{{ file_url }}" class="downloadLink" data-filename="{{ file_name }}">{{ file_name }}</a>
</li>

Ich war süchtig nach

Problem

Diese Konfiguration funktioniert jedoch nicht. Ich habe es nicht bemerkt, bis ich mich versammelt habe. .. ..

Ich habe es nicht bemerkt, bis ich den Fehler tatsächlich zusammengebaut und bestätigt habe.

Bitte überprüfen Sie diese Seite.

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/limits.html

Nutzlast aufrufen (Anfrage und Antwort) 6 MB (synchron)

Was! !! ?? ?? ?? !! !! 6 MB? ?? ?? !! !! ?? ?? ??

Dead end.

~ Schließe ~ </ FONT> ab

Mit EC2 bauen

Ja

Diagramm

Nur EC2. Vorbereitet für Ubuntu 18.04.

Implementierung / Einstellung

Wir haben die Flaschen-API in dem von Docker vorbereiteten Python-Container vorbereitet. Der Prozess der grundlegenden Authentifizierung und des Herunterladens von Zip-Dateien bleibt Bottle überlassen.

Laden Sie die ZIP-Datei herunter, die sich im aktuellen Verzeichnis befindet.

app.py

import bottle


#Benutzername und Passwort für die BASIC-Authentifizierung
USERNAME = "user"
PASSWORD = "pass"


def check(username, password):
    u"""
Überprüfen Sie den Benutzernamen und das Kennwort für die BASIC-Authentifizierung
    @bottle.auth_basic(check)Bewerben in
    """
    return username == USERNAME and password == PASSWORD


@bottle.route("/zip")
@bottle.auth_basic(check)
def zip():
    zip_filename = 'files.zip'
    with open(zip_filename, 'rb') as f:
        body = f.read()

    response.content_type = 'application/zip'
    response.set_header('Content-Disposition', 'attachment; filename="{}"'.format(zip_filename))
    response.set_header('Content-Length', len(body))
    response.body = body
    return response


if __name__ == '__main__':
    bottle.run(host='0.0.0.0', port=80, debug=True)
$ docker run -p 80:80 -v $(pwd):/app -it docker-image-hogehoge python3 /app/app.py

Jetzt können Sie eine URL auch mit großer Kapazität herunterladen (obwohl es einige Zeit dauert)! !! !! !! !! !! !!

Verschiedene Eindrücke

Zum Lernen! Es ist serverlos! Ich war begeistert, aber ich hätte es von Anfang an mit EC2 tun sollen. .. .. .. .. .. ..

Recommended Posts

Erstellt einen Mechanismus zum Herunterladen einer großen Zip-Datei mit Basisauthentifizierung mit aws
Erstellen Sie eine große Textdatei mit Shellscript
Lesen einer CSV-Datei mit Python 2/3
So tarnen Sie eine ZIP-Datei als PNG-Datei
Speichern Sie das Objekt in einer Datei mit pickle
AWS-Schrittfunktionen zum Lernen anhand eines Beispiels
Ich möchte mit Python in eine Datei schreiben
Konvertieren Sie eine Textdatei mit hexadezimalen Werten in eine Binärdatei
Ich habe versucht, Dateien mit Python in AWS S3 / Azure BlobStorage / GCP CloudStorage hochzuladen / herunterzuladen
So setzen Sie einen Hyperlink zu "file: // hogehoge" mit sphinx-> pdf
Ich habe gerade eine virtuelle Umgebung mit der AWS-Lambda-Schicht erstellt