files_streaming()

Streamt die Dateien eines Dokumentobjekts chunk-weise direkt von der noch offenen Verbindung — ohne die Datei komplett im Speicher oder in einer temporären Datei zwischenzuspeichern. Das Gegenstück zu files(), gedacht für große Dokumente, die weitergereicht werden sollen (z. B. an eine HTTP-Antwort oder in eine Datei), ohne lokales Caching. Die aktuellen Dateien werden über std.StoreInCacheById abgerufen.

files_streaming() ist ein Context Manager: Die (gepoolte) Verbindung bleibt für die Dauer des with-Blocks ausgeliehen. Beim Verlassen des Blocks wird die Verbindung sicher behandelt (siehe Verbindungs-Lebenszyklus).

Die Job-Rückgabeparameter, Fehler und return_code stehen sofort beim Betreten des with-Blocks zur Verfügung — noch bevor ein einziges Dateibyte gelesen wurde. Die Pro-Datei-Metadaten (name, size, extension) stehen fest, sobald iter_files() die jeweilige Datei liefert, also vor deren erstem Chunk.

1. Signatur

  • Sync

  • Async

with ecm.dms.files_streaming(
    model: ECMDocumentModel | int,
    *,
    flags: int = 1,
    convert: StoreInCacheByIdConversion = StoreInCacheByIdConversion.NONE,
    when_cold_then_tiff: bool = False,
    add_annotations: bool = False,
) as response:  # JobStreamingResponse
    ...
async with ecm.dms.files_streaming(
    model: ECMDocumentModel | int,
    *,
    flags: int = 1,
    convert: StoreInCacheByIdConversion = StoreInCacheByIdConversion.NONE,
    when_cold_then_tiff: bool = False,
    add_annotations: bool = False,
) as response:  # AsyncJobStreamingResponse
    ...

2. Parameter

Parameter Typ Standard Beschreibung

model

ECMDocumentModel | int

Das Dokument: entweder eine ECMDocumentModel-Instanz (deren id verwendet wird) oder eine reine Objekt-ID als int.

flags

int

1

Übertragungs-Flags: 0 = Dokumentdateien und DIA-Datei, 1 = nur die Dokumentdateien (Standard), 2 = nur die DIA-Datei.

convert

StoreInCacheByIdConversion

NONE

Optionale Konvertierung vor der Übertragung (siehe files()).

when_cold_then_tiff

bool

False

Wenn True, werden ASCII-COLD-Dateien im TIFF-Format zurückgegeben.

add_annotations

bool

False

Wenn True, werden Bild-Annotationen in die zurückgegebenen Dateien eingebrannt.

Anders als files() unterstützt files_streaming() kein version_guid. Für historische Versionen ist weiterhin files() zu verwenden.

3. Rückgabewert

Der Context Manager liefert eine JobStreamingResponse (sync) bzw. AsyncJobStreamingResponse (async).

Table 1. JobStreamingResponse (Auszug)
Member Beschreibung

return_code

Server-Rückgabecode des Jobs (0 = Erfolg).

get(name, type, default=None)

Typisierter Zugriff auf einen Antwortparameter (analog zu JobResult.get).

errors / error_message

Liste der JobError bzw. zusammengefasste Fehlermeldung.

iter_files()

Iterator über die Antwort-Dateien als StreamedResponseFile. Jede Datei muss vollständig gelesen werden, bevor die nächste beginnt.

abort()

Markiert die Antwort als bewusst abgebrochen (kein IncompleteStreamError beim Verlassen).

Table 2. StreamedResponseFile (Auszug)
Member Beschreibung

name / extension / size

Dateimetadaten, bekannt vor dem ersten Chunk.

iter_chunks(chunk_size=65536)

Iterator über die Dateibytes in Chunks (Default 64 KiB). Aktualisiert dabei die laufende SHA-1-Prüfsumme und liest zum Schluss den Datei-Footer.

skip()

Verwirft diese Datei bewusst (wie response.abort()).

4. Verbindungs-Lebenszyklus

Der with-/async with-Block schützt die Verbindung immer:

  • Vollständig gelesen → die abschließende SHA-1-Prüfsumme wird validiert und die Verbindung geht zurück in den Pool.

  • Versehentlich unvollständig gelesen → die Verbindung wird invalidiert (Socket geschlossen) und ein IncompleteStreamError wird geworfen (außer der Block wird ohnehin schon wegen einer anderen Exception verlassen — diese hat Vorrang).

  • Bewusst abgebrochen via response.abort() / file.skip() → die Verbindung wird invalidiert, kein Fehler.

5. Beispiele

5.1. Dokument in eine Datei streamen

  • Sync

  • Async

with ecm.dms.files_streaming(document) as response:
    assert response.return_code == 0
    for attachment in response.iter_files():
        with open(attachment.name, "wb") as out:
            for chunk in attachment.iter_chunks():
                out.write(chunk)  # kein RAM-/Temp-Buffer
async with ecm.dms.files_streaming(document) as response:
    async for attachment in response.iter_files():
        with open(attachment.name, "wb") as out:
            async for chunk in attachment.iter_chunks():
                out.write(chunk)

5.2. Prüfsumme on-the-fly berechnen

import hashlib

digest = hashlib.sha256()
with ecm.dms.files_streaming(document) as response:
    for attachment in response.iter_files():
        for chunk in attachment.iter_chunks(chunk_size=1024 * 1024):
            digest.update(chunk)
print(digest.hexdigest())

5.3. Bewusster Frühabbruch

with ecm.dms.files_streaming(document) as response:
    first = next(response.iter_files())
    header = next(first.iter_chunks())  # nur die ersten Bytes
    response.abort()                    # Rest bewusst verwerfen -> kein Fehler

6. Vergleich mit files() und document_stream()

Aspekt files() document_stream() files_streaming()

Ergebnistyp

Liste von JobResponseFile

bytes (ein Byte-Bereich)

JobStreamingResponse (Context Manager)

Zwischenspeicherung

RAM oder Temp-Datei

RAM (der gelesene Bereich)

Keine — direkt vom Socket

Mehrere Dateien

Ja

Nein

Ja (datei-für-datei)

Server-Job

std.StoreInCacheById

std.GetDocumentStream

std.StoreInCacheById

7. Siehe auch