AnkiIO 1.0.2
Build, validate, import, and export Anki-compatible decks from .NET
Loading...
Searching...
No Matches
AnkiIO.AnkiPackageReader Class Reference

Reads guarded legacy-compatible .apkg archives into AnkiIO's in-memory package model. More...

Static Public Member Functions

static async Task< AnkiPackageReadAsync (string path, AnkiPackageLimits? limits=null, CancellationToken cancellationToken=default)
 Reads a package file without modifying the source file or an Anki profile.
static async Task< AnkiPackageReadAsync (Stream source, AnkiPackageLimits? limits=null, CancellationToken cancellationToken=default)
 Reads a package from a readable, seekable caller-owned stream and leaves it open.

Detailed Description

Reads guarded legacy-compatible .apkg archives into AnkiIO's in-memory package model.

This reader treats the ZIP directory, entry names and sizes, media map, and SQLite database as untrusted. It validates AnkiPackageLimits and archive metadata before extracting the collection database or copying media. Limits mitigate common archive-exhaustion attacks but are not a sandbox; see AnkiPackageLimits for the threat model and choose smaller bounds for network uploads.

The supported input is a ZIP archive containing collection.anki2 and legacy JSON model/deck metadata. Collection schema values 11 and 18 are accepted only when that metadata is still JSON. Modern native entries such as collection.anki21, collection.anki21b, schema-18 protobuf metadata, and meta are not decoded. Anki 26.05 was verified to import the legacy representation written by AnkiIO; this reader does not claim general support for every package exported by that version.

Decks, notes, front/back templates, cards, supported scheduling fields, low three-bit card flags, descriptions, field editor settings, browser-only template formats, and mapped media are reconstructed. Review-log rows, deck configuration, graves, unknown SQLite columns, note/card auxiliary columns, and unsupported model metadata are not preserved. Consult AnkiPackage.Diagnostics after reading.

Media payloads are eagerly copied into AnkiPackage.Media and are not attached to individual decks. The returned package owns those copies and no archive handles, but may retain memory proportional to allowed media size. Separate calls may run concurrently when they use separate streams; returned graphs are mutable and not thread-safe.

var limits = AnkiPackageLimits.Default with { MaximumTotalBytes = 128L * 1024 * 1024 };
var package = await AnkiPackageReader.ReadAsync("deck.apkg", limits);
foreach (var diagnostic in package.Diagnostics)
{
Console.WriteLine($"{diagnostic.Code}: {diagnostic.Message}");
}
static AnkiPackageLimits Default
Gets the shared default limits.

Definition at line 43 of file AnkiPackageReader.cs.

Member Function Documentation

◆ ReadAsync() [1/2]

async Task< AnkiPackage > AnkiIO.AnkiPackageReader.ReadAsync ( Stream source,
AnkiPackageLimits? limits = null,
CancellationToken cancellationToken = default )
inlinestatic

Reads a package from a readable, seekable caller-owned stream and leaves it open.

Parameters
sourceA readable, seekable stream containing the complete package archive.
limitsOptional archive defenses. null selects AnkiPackageLimits.Default.
cancellationTokenCancels extraction, database reads, media copying, and JSON deserialization.
Returns
A task whose result owns the supported deck graph, diagnostics, and copied media registrations.

Position source at the beginning of the complete ZIP archive before calling. The caller retains ownership and the stream remains open on success or failure. Its final position is unspecified and the original position is not restored. Do not read, write, seek, or dispose it concurrently. Seeking is required because the ZIP central directory must be inspected before extraction. The collection database is copied to an isolated temporary directory and removed afterward; media is copied eagerly into the returned package. Cancellation is observed by asynchronous stream, database, and media operations, not every synchronous metadata check.

Exceptions
ArgumentNullExceptionsource is null.
ArgumentExceptionsource is not readable and seekable.
ArgumentOutOfRangeExceptionlimits contains a non-positive count or byte bound, or a non-finite or non-positive compression ratio.
ObjectDisposedExceptionsource has been disposed.
AnkiPackageSecurityExceptionThe archive violates a configured size, ratio, count, path, link, or media-map safety rule.
NotSupportedExceptionThe archive lacks supported legacy collection data or uses unsupported collection metadata.
InvalidDataExceptionThe ZIP data or a supported package relationship is malformed.
JsonExceptionThe media map or legacy JSON metadata is malformed.
Microsoft.Data.Sqlite.SqliteExceptionThe extracted collection is not a readable supported SQLite database.
InvalidOperationExceptionMalformed content creates a conflicting media registration or invalid object relationship.
OutOfMemoryExceptionThe process cannot allocate memory for an extracted media payload.
OverflowExceptionThe aggregate uncompressed archive length cannot be represented by long.
UnauthorizedAccessExceptionThe temporary workspace cannot be accessed.
IOExceptionThe source, a temporary file, or a temporary directory cannot be read, written, or removed.
OperationCanceledExceptioncancellationToken is canceled.

Definition at line 115 of file AnkiPackageReader.cs.

◆ ReadAsync() [2/2]

async Task< AnkiPackage > AnkiIO.AnkiPackageReader.ReadAsync ( string path,
AnkiPackageLimits? limits = null,
CancellationToken cancellationToken = default )
inlinestatic

Reads a package file without modifying the source file or an Anki profile.

Parameters
pathThe path of the legacy-compatible .apkg archive to read.
limitsOptional archive defenses. null selects AnkiPackageLimits.Default.
cancellationTokenCancels extraction, database reads, media copying, and JSON deserialization.
Returns
A task whose result owns the supported deck graph, diagnostics, and copied media registrations.

The source file is opened with read access and file sharing for other readers, and is closed before the task completes. The file is never modified and no Anki profile is opened. The collection database is extracted into an isolated temporary directory that is removed on success, cancellation, or failure. Media is copied eagerly into the returned package. Cancellation is observed by asynchronous file, database, and media operations; synchronous ZIP metadata validation is not individually cancelable.

Exceptions
ArgumentNullExceptionpath is null.
ArgumentExceptionpath is blank or has invalid path syntax.
ArgumentOutOfRangeExceptionlimits contains a non-positive count or byte bound, or a non-finite or non-positive compression ratio.
PathTooLongExceptionpath exceeds a platform path-length limit.
FileNotFoundExceptionThe package file does not exist.
DirectoryNotFoundExceptionA path directory does not exist.
UnauthorizedAccessExceptionThe package or temporary workspace cannot be accessed.
AnkiPackageSecurityExceptionThe archive violates a configured size, ratio, count, path, link, or media-map safety rule.
NotSupportedExceptionThe archive lacks supported legacy collection data or uses unsupported collection metadata.
InvalidDataExceptionThe ZIP data or a supported package relationship is malformed.
JsonExceptionThe media map or legacy JSON metadata is malformed.
Microsoft.Data.Sqlite.SqliteExceptionThe extracted collection is not a readable supported SQLite database.
InvalidOperationExceptionMalformed content creates a conflicting media registration or invalid object relationship.
OutOfMemoryExceptionThe process cannot allocate memory for an extracted media payload.
OverflowExceptionThe aggregate uncompressed archive length cannot be represented by long.
IOExceptionA package, temporary file, or temporary directory cannot be read, written, or removed.
OperationCanceledExceptioncancellationToken is canceled.

Definition at line 76 of file AnkiPackageReader.cs.


The documentation for this class was generated from the following file: