calculate_digest()
Berechnet lokal denselben Digest, den enaio für ein Dokument mit genau diesen Dateien speichert — ohne Serverkontakt und ohne die Dateien hochzuladen.
Damit lässt sich vor einem Upload feststellen, ob eine Datei bereits archiviert ist:
den Wert berechnen und über objects_by_digest()
nachschlagen oder direkt mit table_digest aus digest()
vergleichen. (Ticket #16111)
1. Berechnungsvorschrift
enaio bildet den Digest eines Dokuments nicht über den aneinandergehängten Dateiinhalt, sondern über die Digests der einzelnen Dateien:
-
SHA256 über jede einzelne Datei.
-
Jeder dieser Digests wird als GROSSGESCHRIEBENER Hex-String dargestellt und in Dateireihenfolge zu einem ASCII-String konkateniert.
-
SHA256 über die ASCII-Bytes dieses Strings.
SHA256( "".join(SHA256(datei).hexdigest().upper() for datei in dateien).encode("ascii") )
| Auch ein Dokument mit nur einer Datei ist damit nicht der einfache SHA256 dieser Datei — der Hex-String durchläuft die zweite SHA256-Runde ebenfalls. |
Die Reihenfolge der Dateien geht in den Digest ein. Sie muss der Reihenfolge entsprechen, in der die Dateien an insert() übergeben wurden.
Der Rückgabewert ist grossgeschrieben, wie enaio den Wert speichert und zurückgibt; er
lässt sich damit ohne Normalisierung direkt mit ECMDocumentDigest.table_digest
vergleichen.
2. Signatur
-
Sync
-
Async
ecm.dms.calculate_digest(
files: ECMDigestSource | Iterable[ECMDigestSource],
*,
chunk_size: int = 65536,
) -> str
await ecm.dms.calculate_digest(
files: ECMDigestSource | Iterable[ECMDigestSource],
*,
chunk_size: int = 65536,
) -> str
Die asynchrone Variante sendet ebenfalls keinen Job. Sie ist eine Coroutine, damit
sie sich in den Aufrufstil einfügt und — wichtiger — damit Lesen und Hashen über
asyncio.to_thread in einem Worker-Thread laufen und den Event-Loop bei grossen Dateien
nicht blockieren.
|
3. Parameter
| Parameter | Typ | Standard | Beschreibung |
|---|---|---|---|
|
|
— |
Eine Dateiquelle oder eine iterierbare Folge davon, in der Reihenfolge, in der die Dateien zum Dokument gehören. Eine einzelne Quelle muss nicht in eine Liste verpackt werden. |
|
|
|
Anzahl Bytes pro Lesevorgang bei gestreamten Quellen. Beeinflusst nur den Speicherbedarf, nicht das Ergebnis. |
3.1. Zulässige Dateiquellen
| Typ | Bedeutung |
|---|---|
|
Der Dateiinhalt. |
|
Ein Pfad, aus dem der Inhalt gelesen wird. Ein |
|
Dasselbe Objekt, das an insert() oder checkin() übergeben wird. Ein |
|
Wie von files() zurückgegeben. |
Offener Binärstream |
Wird ab der aktuellen Position bis zum Ende gelesen. |
5. Ausnahmen
| Ausnahme | Bedingung |
|---|---|
|
|
|
|
|
Ein als Pfad übergebener Eintrag existiert nicht. |
6. Beispiele
6.1. Prüfen, ob eine Datei bereits archiviert ist
-
Sync
-
Async
local = ecm.dms.calculate_digest("rechnung.pdf")
if ecm.dms.objects_by_digest(local):
print("bereits abgelegt")
else:
ecm.dms.insert(RechnungsDokument(Titel="Rechnung"), files=[RequestFileFromPath("rechnung.pdf")])
local = await ecm.dms.calculate_digest("rechnung.pdf")
if await ecm.dms.objects_by_digest(local):
print("bereits abgelegt")
else:
await ecm.dms.insert(RechnungsDokument(Titel="Rechnung"), files=[RequestFileFromPath("rechnung.pdf")])
6.2. Gegen ein bekanntes Dokument vergleichen
-
Sync
-
Async
if ecm.dms.calculate_digest("rechnung.pdf") == ecm.dms.digest(doc).table_digest:
print("identisch mit dem archivierten Dokument")
if await ecm.dms.calculate_digest("rechnung.pdf") == (await ecm.dms.digest(doc)).table_digest:
print("identisch mit dem archivierten Dokument")
6.3. Mehrere Dateien — die Reihenfolge zählt
# Die Reihenfolge muss der beim insert() entsprechen
digest = ecm.dms.calculate_digest(["seite1.png", "seite2.png"])
6.4. Direkt aus den Dateien, die eingefügt werden sollen
from ecmind_blue_client.rpc import JobRequestFileFromPath
files = [JobRequestFileFromPath("seite1.png"), JobRequestFileFromPath("seite2.png")]
if not ecm.dms.objects_by_digest(ecm.dms.calculate_digest(files)):
ecm.dms.insert(MeinDokument(Name="Scan"), files=files)
7. Hinweise
-
Die Berechnung liest Dateien in Blöcken. Ein mehrere Gigabyte grosses Dokument kostet einen Block Speicher, nicht seine Grösse.
-
Die serverseitige Suche akzeptiert Gross- und Kleinschreibung. Ein Digest aus einem Fremdsystem muss also nicht umgestellt werden, bevor er nachgeschlagen wird.
-
Verifiziert gegen einen 12.0-Server für Dokumente mit einer, zwei und drei Dateien.
8. Siehe auch
-
objects_by_digest() — Dokumente zu einem Digest suchen
-
digest() — den serverseitig gespeicherten Digest lesen
-
files() — Dateien eines Dokuments herunterladen