PackagePart.GetStream() devolve um fluxo não procurável para partes comprimidas em pacotes ReadWrite

Quando abres um Package com System.IO.FileAccess.ReadWrite?displayProperty=nameWithType> e depois abres uma parte comprimida para leitura que não foi modificada na sessão atual, PackagePart.GetStream agora retorna um fluxo apenas avançado em vez de um objeto de busca MemoryStream<.

Versão introduzida

.NET 11 Pré-visualização 7

Comportamento anterior

Anteriormente, quando se abria um Package com FileAccess.ReadWrite (que mapeia internamente para ZipArchiveMode.Update), abrir uma parte comprimida para leitura devolveu um objeto MemoryStream de busca que continha o conteúdo da entrada totalmente descomprimido.

using Package package = Package.Open("file.docx", FileMode.Open, FileAccess.ReadWrite);
PackagePart part = package.GetPart(new Uri("/word/document.xml", UriKind.Relative));

// Returned a seekable MemoryStream.
using Stream stream = part.GetStream(FileMode.Open, FileAccess.Read);
Console.WriteLine(stream.CanSeek);   // true
stream.Seek(0, SeekOrigin.Begin);    // succeeded
Console.WriteLine(stream.Position);  // 0

Novo comportamento

A partir do .NET 11, a mesma chamada devolve um fluxo apenas encaminhado (não procurável) para partes comprimidas que não foram modificadas na sessão atual.

using Package package = Package.Open("file.docx", FileMode.Open, FileAccess.ReadWrite);
PackagePart part = package.GetPart(new Uri("/word/document.xml", UriKind.Relative));

// Returns a forward-only (non-seekable) stream.
using Stream stream = part.GetStream(FileMode.Open, FileAccess.Read);
Console.WriteLine(stream.CanSeek);   // false
stream.Seek(0, SeekOrigin.Begin);    // throws NotSupportedException
stream.Position = 0;                 // throws NotSupportedException
Console.WriteLine(stream.Length);    // still works (reported from entry metadata)

Todas as seguintes condições devem ser verdadeiras simultaneamente para que possa observar esta mudança:

  • O pacote é aberto com FileAccess.ReadWrite (Package.Open(..., FileAccess.ReadWrite)).
  • A parte está aberta apenas para leitura (GetStream(FileMode.Open, FileAccess.Read)).
  • A peça é comprimida (CompressionOption exceto NotCompressed).
  • A parte não foi escrita nem modificada anteriormente na mesma sessão.
  • O consumidor procura incondicionalmente o fluxo ou lê Position.

Os seguintes cenários não são afetados:

  • Pacotes de apenas leitura (FileAccess.Read): Os seus fluxos de peças comprimidos já eram apenas para avançar.
  • Peças não comprimidas (Stored): Permanecem procuráveis.
  • Partes modificadas mais cedo na sessão atual: São servidas a partir de um instantâneo em memória, por isso permanecem procuráveis.
  • Aceder Stream.Lengtha .
  • Consumidores apenas forward-only (o caso comum, como XmlReader, XDocument.Load, o SDK Open XML, e CopyTo).
  • Frameworks alvo anteriores ao .NET 11: A otimização está bloqueada por NET11_0_OR_GREATERtrás de .

Tipo de mudança disruptiva

Esta mudança é uma mudança comportamental.

Motivo da mudança

Anteriormente, abrir uma parte comprimida para ler de um ReadWrite pacote descomprimia sempre toda a entrada em a MemoryStream antes de devolver o fluxo. Esta abordagem produziu um fluxo procurável, mas impunha alocações desnecessárias de memória e sobrecarga de CPU para o caso comum em que os chamadores apenas leem o fluxo sequencialmente (por exemplo, XmlReader ou o Open XML SDK). O novo comportamento é transmitido diretamente da entrada subjacente do arquivo ZIP para leituras apenas diretas, o que evita a descompressão inicial.

Para mais informações, consulte dotnet/runtime#129698.

Se o seu código requer um fluxo procurável de uma parte comprimida de um ReadWrite pacote, copie-o manualmente MemoryStream :

using Stream partStream = part.GetStream(FileMode.Open, FileAccess.Read);
using MemoryStream seekable = new();
partStream.CopyTo(seekable);
seekable.Position = 0;
// Use 'seekable'. It's fully buffered and seekable.

Alternativamente, se o seu caso de uso não exigir ReadWrite acesso ao pacote, abre-o em vez disso.FileAccess.Read Os pacotes apenas de leitura já devolvevam fluxos apenas avançados antes desta alteração, pelo que este modo evita surpresas para código sensível à procura.

APIs afetadas