diff --git a/.gitignore b/.gitignore
index f5905ad..9d61207 100644
--- a/.gitignore
+++ b/.gitignore
@@ -168,5 +168,5 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
-_identifierAllocation.db
-data/
\ No newline at end of file
+data/
+_map.db
\ No newline at end of file
diff --git a/README.md b/README.md
index 9ff4b1b..36f8173 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,35 @@
-Dieses Projekt demonstriert die Umsetzung eines einfaches Dienstes mit Python 3, Flask, sqlite3 und Caddy welches ermöglicht das Hochladen/Herunterladen von Dateien über Weboberfläche oder API.
+Dieses Projekt demonstriert die Umsetzung eines einfaches Dienstes mit flask (Routing), jinja2 (Templates) und sqlite3 in Python welches ermöglicht das Hochladen/Herunterladen von Dateien über Weboberfläche oder URL abruf.
## Pre-Installation
1. Installiere Abhängigkeiten.
- Debian-basierte Distributionen: sudo apt install caddy python
+ pip install flask jinja2 sqlite3
- pip install flask sqlite3
+2. Klone die Repository
+ git clone https://git.raizen.me/raizen/E3FI2_Kamil_Filehosting.git
-2. Editiere die Konfigurationsdatei von Caddy
- nano /etc/caddy/Caddyfile
+3. Führe das Skript aus
+ flask --app main.py run
+ oder
+ python -m flask --app main.py run
+4. In Webbrowser http://127.0.0.1:5000 öffen
+
+
+## Bedienung
+Aus Perspektive der Nutzer:
+ 1. Rufe index auf
+ 2. Häge die Datei an das Formular, drücke auf Upload
+ 3. Eine Übersicht mit Downloadlink wird angezeigt
+
+Aus Perspektive der API:
+ 1. Übergebe die Datei an das /upload Endpoint (format: file -> binary data, filename -> name der datei)
+ 2. Aufruf von Downloads genau so wie Nutzer
+
+
+## Struktur
+
+(host)/ -> Formular bei dem die Datei angehängt wird (nicht für API gedacht)
+(host)/upload -> Formular oder API sendet die Datei an dieses Endpoint, als Rückgabe wird eine übersicht mit dem Link ausgegeben.
+(host)/file/(id) -> Dateiübersicht die Name, Größe, Uploaddatum sowie den Downloadlink enthält (könnte für API nützlich sein)
+(host)/file/(id)/download -> Startet den download von der Datei
diff --git a/_map.db b/_map.db
index 744bf75..71ca0fa 100644
Binary files a/_map.db and b/_map.db differ
diff --git a/main.py b/main.py
index e32f00c..a0c5924 100644
--- a/main.py
+++ b/main.py
@@ -1,63 +1,110 @@
-from flask import send_file, Flask, request
-#from werkzeug.middleware.proxy_fix import ProxyFix # Für Einsatz in Produktion
-import sqlite3, hashlib, random, os, pathlib
+from flask import send_file, Flask, request, jsonify
+#from werkzeug.middleware.proxy_fix import ProxyFix # Für Einsatz in Produktion hinter einem Webserver
+import sqlite3, hashlib, random, os, pathlib, jinja2
+
+prefixes = ["B", "KB", "MB", "GB", "TB"]
app = Flask(__name__)
-#app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1) # Für Einsatz in Produktion
+#app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1) # Für Einsatz in Produktion hinter einem Webserver
-db = sqlite3.connect("_map.db", check_same_thread=False)
-dbCursor = db.cursor()
-dbCursor.execute("CREATE TABLE IF NOT EXISTS _idToFile (id TEXT PRIMARY KEY, destination TEXT, uploadTime DATETIME, fileName TEXT, usesLeft INT);")
+jinjaEnv = jinja2.Environment(loader=jinja2.PackageLoader("main", "pages"))
+downloadTemplate = jinjaEnv.get_template("download.html")
+indexTemplate = jinjaEnv.get_template("index.html")
+errorTemplate = jinjaEnv.get_template("error.html")
+uploadedTemplate = jinjaEnv.get_template("uploaded.html")
-if pathlib.Path("./data").is_dir() == False:
+
+db = sqlite3.connect("_map.db", check_same_thread=False) # Erzeuge oder/und baue eine Verbindung auf zu der Datenbank (check_same_thread muss False sein da Flask mehrere eigene Threads verwendet)
+dbCursor = db.cursor() # Keine Ahnung warum explizit ein Zeiger erzeugt werden muss, die meisten der Bibliotheken verwalten das von alleine
+dbCursor.execute("CREATE TABLE IF NOT EXISTS _idToFile (id TEXT PRIMARY KEY, destination TEXT, uploadTime DATETIME, fileName TEXT);") # Selbsterklärend
+
+# Erzeuge ein Ordner (falls dieser nicht exisitert) wo die Dateien abgelegt werden
+if pathlib.Path("./data").is_dir() == False:
os.mkdir("data")
+# Flask dings zeug
@app.route("/")
def mainpage():
- return send_file("index.html")
+ return indexTemplate.render()
@app.route("/upload", methods = ["POST"])
def uploader():
- file = request.files['file'] #
- name = file.filename.rsplit(".", 1)
+ file = request.files['file'] # Hole die Datei als FileStorage Objekt
+ name = file.filename.rsplit(".", 1) #
idValue = ""
idIsUnique = False
- while idIsUnique == False:
+ # Solange die ID nicht eindeutig ist, versuche eine neue zu erzeugen und überprüfe es (wird in 99,99% Fällen nur ein mal durchgelaufen)
+ while idIsUnique == False:
- if idValue != "":
+ if idValue != "": # Ich könnte das if weglassen aber keine Ahnung ich finde das so besser
idValue = ""
+ # Fancy weg um eine 14-stellige ID zu erzeugen, wahrscheinlich gibt es fertige Funktionen aber wollte trotzdem eigene haben
for _ in range(14):
- vArray = [""] * 3
- vArray[0] = chr(random.randint(48, 57)) # 0 - 9
- vArray[1] = chr(random.randint(65, 90)) # A - Z
- vArray[2] = chr(random.randint(97, 122)) # a - z
+ vArray = [""] * 3 # Fancy weg um ein Array mit der Länge 3 zu erzeugen
+ vArray[0] = chr(random.randint(48, 57)) # 0 - 9 in Unicode Charset
+ vArray[1] = chr(random.randint(65, 90)) # A - Z in Unicode Charset
+ vArray[2] = chr(random.randint(97, 122)) # a - z in Unicode Charset
- selector = random.randint(0,2)
- idValue += vArray[selector]
+ # Aus 3 generierten Werten wähle einen aus und füge es am Ende des Strings an
+ selector = random.randint(0,2)
+ idValue += vArray[selector]
- dbCursor.execute("SELECT * FROM _idToFile WHERE id=?;", (idValue,))
+ # Überprüfe ob die erstellte ID bereits existiert, falls ja, dann führe die Schleife nochmal durch
+ dbCursor.execute("SELECT * FROM _idToFile WHERE id=?;", (idValue,))
if dbCursor.fetchone() == None:
idIsUnique = True
if (len(name) > 1): # Dateinamen die ein Suffix besitzen
- path = f"./data/{hashlib.sha256((name[0] + idValue).encode()).hexdigest()}.{name[1]}"
+ path = f"./data/{hashlib.sha256((name[0] + idValue).encode()).hexdigest()}.{name[1]}" # Wirkt wie schwarze Magie ist aber sehr simpel
else: # Dateinamen die kein Suffix besitzen
path = f"./data/{hashlib.sha256((name[0] + idValue).encode()).hexdigest()}"
file.save(path)
- dbCursor.execute("INSERT INTO _idToFile VALUES (?, ?, datetime('now'), ?, ?);", (idValue, path, file.filename, 10))
- db.commit()
+ dbCursor.execute("INSERT INTO _idToFile VALUES (?, ?, datetime('now'), ?);", (idValue, path, file.filename,)) # Füge neuen Eintrag in die Datenbank
+ db.commit() # Bestätige die Transaktion damit der Eintrag tatsächlich gespeichert wird. Das beste ist ja, dass ich es nur bei INSERT tätigen muss
- return "sank you"
+ if request.user_agent.string != "API":
+ return uploadedTemplate.render(link=("/file/" + idValue))
+ else:
+ return jsonify(status=100,id=idValue)
-@app.route("/download/ Größe: {{ size }} Uploaddatum: {{ uploadDate }} {{ error }}{{ filename }}
+
+ Download
+
+
\ No newline at end of file
diff --git a/pages/error.html b/pages/error.html
new file mode 100644
index 0000000..9ff7074
--- /dev/null
+++ b/pages/error.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ WEE WOO WEE WOO! WE FOUND AN ERROR!
+
Link: {{ link }}
+diff --git a/pages/uploaded.html b/pages/uploaded.html new file mode 100644 index 0000000..1fbd6bf --- /dev/null +++ b/pages/uploaded.html @@ -0,0 +1,15 @@ + + +
+ + +
+ +
+