Zum Inhalt

upload: Streaming-Leser und Parser für das Hochladen von Dateien über HTTP basierend auf dem nginx-module-lua cosocket

Installation

Wenn Sie das RPM-Repository-Abonnement noch nicht eingerichtet haben, melden Sie sich an. Dann können Sie mit den folgenden Schritten fortfahren.

CentOS/RHEL 7 oder Amazon Linux 2

yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-upload

CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-upload

Um diese Lua-Bibliothek mit NGINX zu verwenden, stellen Sie sicher, dass nginx-module-lua installiert ist.

Dieses Dokument beschreibt lua-resty-upload v0.11, das am 19. Januar 2023 veröffentlicht wurde.


Diese Lua-Bibliothek ist eine Streaming-API für das Hochladen von Dateien für das ngx_lua nginx-Modul:

http://wiki.nginx.org/HttpLuaModule

Der MIME-Typ multipart/form-data wird unterstützt.

Die API dieser Bibliothek gibt Tokens nacheinander zurück. Der Benutzer muss lediglich die read-Methode wiederholt aufrufen, bis ein nil-Token-Typ zurückgegeben wird. Für jedes Token, das von der read-Methode zurückgegeben wird, überprüfen Sie einfach den ersten Rückgabewert auf den aktuellen Token-Typ. Der Token-Typ kann header, body und part end sein. Jedes analysierte multipart/form-data-Formularfeld besteht aus mehreren header-Tokens, die jeden Feldheader halten, mehreren body-Tokens, die jeden Datenchunk des Körpers halten, und einem part end-Flag, das das Ende des Feldes anzeigt.

So funktioniert das Streaming-Lesen. Selbst bei Gigabyte großen Datei-Daten-Eingaben kann der in der Lua-Welt verwendete Speicher klein und konstant sein, solange der Benutzer die Eingabedatenchunks nicht selbst ansammelt.

Diese Lua-Bibliothek nutzt die cosocket-API von ngx_lua, die 100% nicht blockierendes Verhalten gewährleistet.

Beachten Sie, dass mindestens ngx_lua 0.7.9 oder OpenResty 1.2.4.14 erforderlich ist.

Synopsis

    server {
        location /test {
            content_by_lua '
                local upload = require "resty.upload"
                local cjson = require "cjson"

                local chunk_size = 5 -- sollte auf 4096 oder 8192 gesetzt werden
                                     -- für reale Einstellungen

                local form, err = upload:new(chunk_size)
                if not form then
                    ngx.log(ngx.ERR, "fehlgeschlagen beim neuen Upload: ", err)
                    ngx.exit(500)
                end

                form:set_timeout(1000) -- 1 Sekunde

                while true do
                    local typ, res, err = form:read()
                    if not typ then
                        ngx.say("fehlgeschlagen beim Lesen: ", err)
                        return
                    end

                    ngx.say("gelesen: ", cjson.encode({typ, res}))

                    if typ == "eof" then
                        break
                    end
                end

                local typ, res, err = form:read()
                ngx.say("gelesen: ", cjson.encode({typ, res}))
            ';
        }
    }

Eine typische Ausgabe des oben definierten /test-Standorts ist:

gelesen: ["header",["Content-Disposition","form-data; name=\"file1\"; filename=\"a.txt\"","Content-Disposition: form-data; name=\"file1\"; filename=\"a.txt\""]]
gelesen: ["header",["Content-Type","text\/plain","Content-Type: text\/plain"]]
gelesen: ["body","Hello"]
gelesen: ["body",", wor"]
gelesen: ["body","ld"]
gelesen: ["part_end"]
gelesen: ["header",["Content-Disposition","form-data; name=\"test\"","Content-Disposition: form-data; name=\"test\""]]
gelesen: ["body","value"]
gelesen: ["body","\r\n"]
gelesen: ["part_end"]
gelesen: ["eof"]
gelesen: ["eof"]

Sie können die lua-resty-string Bibliothek verwenden, um SHA-1 und MD5-Digests der Dateidaten inkrementell zu berechnen. Hier ist ein solches Beispiel:

    local resty_sha1 = require "resty.sha1"
    local upload = require "resty.upload"

    local chunk_size = 4096
    local form = upload:new(chunk_size)
    local sha1 = resty_sha1:new()
    local file
    while true do
        local typ, res, err = form:read()

        if not typ then
             ngx.say("fehlgeschlagen beim Lesen: ", err)
             return
        end

        if typ == "header" then
            local file_name = my_get_file_name(res)
            if file_name then
                file = io.open(file_name, "w+")
                if not file then
                    ngx.say("fehlgeschlagen beim Öffnen der Datei ", file_name)
                    return
                end
            end

         elseif typ == "body" then
            if file then
                file:write(res)
                sha1:update(res)
            end

        elseif typ == "part_end" then
            file:close()
            file = nil
            local sha1_sum = sha1:final()
            sha1:reset()
            my_save_sha1_sum(sha1_sum)

        elseif typ == "eof" then
            break

        else
            -- nichts tun
        end
    end

Wenn Sie MD5-Prüfziffern für die hochgeladenen Dateien berechnen möchten, verwenden Sie einfach das resty.md5-Modul, das von der lua-resty-string Bibliothek bereitgestellt wird. Es hat eine ähnliche API wie resty.sha1.

Für das Hochladen großer Dateien ist es wichtig, nicht alle Daten im Speicher zu puffern. Das bedeutet, dass Sie Datenchunks niemals in einem riesigen Lua-String oder in einer riesigen Lua-Tabelle ansammeln sollten. Sie müssen die Datenchunks so schnell wie möglich in Dateien schreiben und die Datenchunks sofort wegwerfen (um den Lua-GC sie freigeben zu lassen).

Anstatt die Datenchunks in Dateien zu schreiben (wie im obigen Beispiel gezeigt), können Sie die Datenchunks auch an upstream cosocket-Verbindungen schreiben, wenn Sie die Daten nicht auf lokalen Dateisystemen speichern möchten.

Verwendung

local upload = require "resty.upload"
local form, err = upload:new(self, chunk_size, max_line_size, preserve_body)
chunk_size hat standardmäßig den Wert 4096. Es ist die Größe, die verwendet wird, um Daten vom Socket zu lesen.

max_line_size hat standardmäßig den Wert 512. Es ist die Größenbeschränkung zum Lesen des chunked Body-Headers.

Standardmäßig wird lua-resty-upload den Anfragekörper konsumieren. Für den Proxy-Modus bedeutet dies, dass der Upstream den Körper nicht sieht. Wenn preserve_body auf true gesetzt ist, wird der Anfragekörper beibehalten. Beachten Sie, dass diese Option nicht kostenlos ist. Wenn sie aktiviert ist, verdoppelt sie den Speicherverbrauch von resty.upload.

Siehe auch

GitHub

Sie finden zusätzliche Konfigurationstipps und Dokumentationen für dieses Modul im GitHub-Repository für nginx-module-upload.