Gäste laden Fotos und Videos von jedem Telefon hoch, und die Dateien bleiben auf dem eigenen Server des Kunden

Hochzeitsgäste scannen einen QR-Code, öffnen eine geheime Adresse und laden Fotos und Videos direkt vom Telefon hoch: keine App, kein Konto, keine Anmeldung. Dateien bis 5 GB landen auf der Festplatte des Kunden, und das Paar sieht zu, wie sich die Galerie während der Feier füllt. Ein Abend, keine zweite Chance, und Telefone von vor sechs Jahren.

  • Dateien bis 5 GB, fortsetzbar
  • Browser von vor sechs Jahren, auf echten Engines getestet
  • Alles auf dem eigenen Server des Kunden, mit einem Ansible-Befehl eingerichtet
Ein Telefon mit QR-Code, eine in Stücke geteilte Datei, das letzte orange, drei parallele Ströme zu einem Server mit eigener Festplatte und ein Galerieraster, das sich füllt, mit einer neuen orangen Kachel

Ein Privatkunde: eine Hochzeitsfeier mit Gästen zwischen 20 und 80 Jahren und jedem Telefon, das sie dabeihatten

Ausgangslage

Fünf Annahmen haben das Projekt geprägt: bis 5 GB je Datei, schwaches WLAN an den Telefonen, Gäste jeden Alters mit mehrere Jahre alten Browsern, Daten, die beim Kunden bleiben, und eine Nacht ohne zweite Chance.

Jede davon hatte eine technische Folge: Upload in Stücken mit Fortsetzung, Streaming auf die Festplatte statt Zwischenspeichern, eine Fassung, die ohne JavaScript funktioniert, der eigene Server des Kunden als Code eingerichtet, und Tests auf echten alten Browsern statt in der Emulation.

Lösung

Drei Container hinter nginx: ein Next.js-Frontend in TypeScript, eine Upload-Engine in reinem TypeScript getrennt von der Oberfläche, und ein FastAPI-Backend, das Stücke auf die Festplatte streamt, die Metadaten führt und die Galerie mit ffmpeg aufbereitet. Der geheime Token aus dem QR-Code ist Teil jeder Adresse und autorisiert jede Anfrage; es gibt keine Konten, Sitzungen oder Cookies.

Der Server wird mit Ansible vorbereitet, bereitgestellt und gewartet: ein Befehl, Prüfungen vor der Änderung, Geheimnisse in einem verschlüsselten Tresor, und die Bereitstellung gilt erst als gelungen, wenn Backend, nginx und die Seite antworten.

Ergebnisse

Je Datei
Bis 5 GB, in Stücken von 8 MB mit Fortsetzung nach Verbindungsabbruch
Parallele Ströme je Upload
3, mit Wiederholungen in wachsendem Abstand
Browserversionen
6 Jahre zurück, auf echten Engines mit Prüfsumme der Datei geprüft
Externe Dienste
0: Die Dateien bleiben auf der 14-TB-Festplatte des Kunden
Einrichtung und Updates des Servers
1 Ansible-Befehl, mit Prüfungen vorher und nachher
Ohne den geheimen Link
404 für die Startseite und jede falsche Adresse

So sind wir vorgegangen

  1. Zuerst die Annahmen

    Bis 5 GB je Datei, schwaches WLAN, Gäste von 20 bis 80 mit jedem Telefon, Daten beim Kunden, ein Abend. Jede Annahme bekam ihre technische Folge vor der ersten Zeile Code.

  2. Drei Container, klare Rollen

    nginx als einziger offener Port und streamende Eingangstür, das Backend als einziger Schreiber auf die Datenplatte, das Frontend ohne Zugriff darauf.

  3. Eine eigene Upload-Engine

    Reines TypeScript ohne Framework: Stückelung, Parallelität, Wiederholungen, Fortsetzung. Die API ist schlichtes HTTP, derselbe Upload funktioniert von der Kommandozeile, und die Diagnose braucht keinen Browser.

  4. Server als Code

    Ansible bereitet den Server vor, stellt mit Prüfungen vor der Änderung bereit und verifiziert das Ergebnis. Das Löschen von Testmaterial verlangt eine ausdrückliche Bestätigung und prüft Festplatte und Verzeichnis.

  5. Tests auf echten Engines

    Archivierte Browserversionen direkt gesteuert, jede über den vollen Weg: Seite öffnen, echte Datei hochladen, Prüfsumme vergleichen, Galerie öffnen.

  6. Ein Sicherheitsnetz ohne JavaScript

    Startet die Anwendung nicht, bekommt der Gast trotzdem ein einfaches HTML-Formular, und der Server speichert die Datei genau wie auf dem normalen Weg.

Was die Annahmen technisch bedeuteten

Annahme Technische Folge
Bis 5 GB je Datei Upload in Stücken mit Fortsetzung, Streaming auf die Festplatte statt Zwischenspeichern
Telefone, oft in schwachem WLAN Toleranz für Verbindungsabbrüche, Wiederholungen, Fortsetzung nach dem Neuladen der Seite
Gäste von 20 bis 80, jedes Telefon Unterstützung mehrere Jahre alter Browser und eine Fassung, die ohne JavaScript funktioniert
Daten bleiben beim Kunden Eigener Server mit 14-TB-Festplatte, Bereitstellung mit Ansible, keine externen Dienste
Eine Nacht, keine zweite Chance Tests auf echten alten Browsern, nicht nur in der Emulation

Herausforderungen, die wir gelöst haben

Jeder Punkt unten hat zwischen einigen Stunden und zwei Tagen gekostet, und keiner ließ sich aus der Dokumentation vorhersehen.

Das iPhone verschlechtert still, was es hochlädt

Ein in 4K aufgenommenes Video kam als kurze Datei in 480 mal 360 an, und Fotos waren kleiner als die Originale. Nichts in der Anwendung tat das. Die WebKit-Quellen machen es klar: Wird eine Datei aus der Fotomediathek gewählt, kodiert Safari auf iOS Videos immer neu, merkt sich die im Auswahldialog gewählte Größe von jeder anderen Seite und übergibt ein Live Photo als seinen kurzen Clip in niedriger Auflösung. Eine Webseite kann das nicht abschalten. Also sagt die Anwendung die Wahrheit: Nach der Auswahl zeigt sie die tatsächliche Auflösung und Länge, warnt bei Anzeichen für eine Verkleinerung und bietet den Weg über die App Dateien an, die das Original liefert. Die Maße jeder Datei werden auf dem Server gespeichert, die Frage, warum ein Video schlecht ist, entscheidet also ein Eintrag und nicht eine Vermutung.

Hängen bei 96 Prozent: eine Antwort, die ankommt, aber nie gesehen wird

Uploads von iPhones erreichten 96 bis 99 Prozent und blieben stehen. Die Serverlogs zeigten alles empfangen und geschrieben, mit anderthalb Sekunden Antwortzeit. Das Telefon wartete endlos. Zwei Ursachen, beide in der Netzwerkschicht von Apple: Der Browser verlor sich, wenn der Anfragekörper direkt aus einer Datei auf der Platte gelesen wurde, und bei mehreren Uploads zugleich erhielt eine Anfrage ein vollständiges HTTP 200, meldete aber nie, dass sie fertig sei. Stücke werden jetzt zuerst in den Speicher gelesen und als fertiger Puffer gesendet; ist eine vollständige, gültige Antwort sichtbar, gilt das Stück als zugestellt und die Zombie-Anfrage wird verworfen; ein Leerlaufzähler von 30 Sekunden löst eine Wiederholung mit wachsendem Abstand aus. Das Telefon schickt am Ende außerdem ein kurzes Ereignisprotokoll, das bei den Metadaten der Datei liegt, sodass sich das Verhalten eines fremden Telefons später am Server nachlesen lässt.

Ein moderner Build, ein altes Telefon, eine weiße Seite

Auf einem einige Jahre alten Telefon mit veraltetem Browser erschien die Seite ohne Stile, und der Upload funktionierte nicht. Das Framework baut für Browser der letzten zwei Jahre, die Stilbibliothek packt alles in eine moderne CSS-Konstruktion, die eine ältere Engine samt dem ganzen Stylesheet überspringt, und einige neue Einheiten und Syntaxkürzel stoppen das ganze Skript. Zwei Fallen obendrauf: Der CSS-Minifizierer entfernte den älteren von zwei Ersatzwerten als Duplikat, und der offizielle Mechanismus des Frameworks, ein Skript zuerst zu laden, lud es nach den eigenen Paketen. Die Lösung: eine ausdrückliche, mehrere Jahre zurückgesetzte Liste unterstützter Browser, die moderne Konstruktion beim Bauen aufgelöst, Ersatzeinheiten, Polyfills und ein paar Zeilen Skript direkt im Kopf des Dokuments.

Eine große Datei gegen die Standardwerte des Servers

Bei 5 GB je Datei arbeiten die meisten Standardwerte gegen einen. Der Reverse-Proxy puffert die ganze Anfrage, bevor er sie weitergibt, und schreibt Gigabytes auf die eigene Platte, nur um sie weiterzureichen; Größenlimits und Zeitüberschreitungen sind kurz. Der Upload streamt jetzt durch alle Schichten ohne Zwischenpuffer, jedes Stück landet an seinem Platz in einer dünn besetzten Datei, der Abschluss ist atomar, und ein nach Verbindungsabbruch wiederholter Abschluss erzeugt kein Duplikat. Die Standardbibliothek für Formulare in Python dekodierte Dateinamen in einer alten westeuropäischen Kodierung, polnische Zeichen konnten so die ganze Anfrage zum Scheitern bringen; ein kurzer Leser der Kopfzeile mit der richtigen Kodierung hat sie ersetzt.

Einen Browser von vor sechs Jahren testen

Moderne Testwerkzeuge können Browser aus dieser Zeit nicht mehr steuern. Statt anzunehmen, es müsse funktionieren, wurden archivierte Engine-Versionen geladen und direkt über ein Protokoll niedrigerer Ebene gesteuert, jede über den vollen Weg: Seite öffnen, echte Datei hochladen, Prüfsumme mit dem Original vergleichen, Galerie und Unterseiten öffnen.

Ein Telefon und ein Fortschrittsbalken, der am letzten, orangen Segment hängt; der Server ist mit einem orangen Kreuz markiert, und eine orange Schleife unter dem Balken steht für die Wiederholung

Was wir daraus mitnehmen

  • Die teuersten Fehler stecken nicht im Code, sondern in fremden Standardwerten. Ein Framework, das für die neuesten Browser baut, ein Minifizierer, der einen Kompatibilitätsersatz entfernt, ein Proxy, der Gigabytes puffert, eine Bibliothek, die Dateinamen in der falschen Kodierung liest: Jede dieser Entscheidungen wurde für uns getroffen, und keine hat sich als Fehler gemeldet.
  • „Bei mir funktioniert es“ ist kein Testergebnis. Die Hälfte der Probleme zeigte sich nur auf echter Hardware: einem bestimmten Telefon, einem bestimmten Browser, einem bestimmten Netz. Deshalb testen wir auf echten Versionen und lassen die Anwendung Belege von Geräten sammeln, an die wir nicht herankommen.
  • Was sich nicht beheben lässt, muss gezeigt werden. Manche Grenzen umgeht keine Webseite. Dann ist es Aufgabe der Anwendung, dem Nutzer die Wahrheit zu sagen und den Weg außen herum zu zeigen.
Ein neues Telefon, ein altes Tastentelefon und ein Laptop erreichen auf drei gleichmäßigen Bahnen denselben Server, markiert mit drei Häkchen; neues Telefon und Laptop gehen über die Anwendung, das alte Telefon über ein orange gezeichnetes einfaches Formular

Verwandte Ausgangslagen

  • Ihr Frontend hängt an jQuery 1.x oder 2.x und an Smarty-Templates, die niemand gern anfasst? Wir erneuern es Bereich für Bereich neben dem laufenden System und testen nach jedem Schritt. Die Zweige 1.x und 2.x unterstützt das jQuery-Projekt nicht mehr (Stand: 2026-10).

  • Wir ziehen Ihre Lucee-, ColdFusion- oder PHP-Anwendung vom klassischen Hosting in Container um, ohne sie neu zu schreiben. So lässt sich die Umgebung nachbauen, aktualisieren und verlegen. Manche Hoster berechnen Aufschläge für alte PHP-Versionen, für PHP 8.2 enden die Sicherheits-Updates am 31.12.2026 (Stand: 2026-10).

  • Ihre Daten wandern heute von Hand zwischen zwei Systemen, oder ein Partner verlangt eine Schnittstelle? Wir verbinden bestehende Anwendungen über REST oder SOAP, gleichen Daten zwischen ihnen ab und versenden Transaktionsmails und Benachrichtigungen aus Ihren Abläufen.

Weitere Projekte

  • Eine Punkteskala mit gestrichelter Schwelle: Der Phishing-Umschlag wandert in einem orangen Bogen über die Schwelle

    Ein Spamfilter, der lief, aber nicht schützte

    Dienste liefen, die Konfiguration war gültig, im Systemstatus stand kein Fehler. Trotzdem landete Phishing im Posteingang, während Newsletter von Partnern im Spam lagen. Unsere Ingenieure haben gemessen, welche Erkennungsmechanismen tatsächlich ansprangen, die Ursachen behoben und drei Monate später eine Regression gefunden und behoben, die sich nur stundenweise zeigte.

  • Vier Briefumschläge passieren die Tore SPF, DKIM und DMARC, jeweils orange abgehakt, auf dem Weg in den Posteingang

    E-Mail-Zustellbarkeit für vier Firmendomains

    Ein Mailserver für vier Firmendomains signierte nichts, veröffentlichte keine DMARC-Richtlinie und wurde von Microsoft abgewiesen. Unsere Ingenieure haben ihn geprüft, die Änderung an einer Domain erprobt und in Gruppen ausgerollt. Das Ergebnis: Alle drei Prüfungen bestehen bei Gmail, die Microsoft-Sperre ist aufgehoben, und die Nutzer haben nichts bemerkt.

In 20 Minuten klären wir, welcher erste Schritt zu Ihrem System passt

Sie schildern Ziel und Stand, wir sagen offen, ob und wie wir helfen können. Antwort innerhalb eines Werktags, unverbindlich.

Telefon (Deutsch, Polnisch, Englisch) +48 501 250 220
E-Mail E-Mail

Erreichbar: Montag bis Freitag, 9:00 bis 17:00 Uhr (MEZ); Support und Notfälle rund um die Uhr (24/7)