PackagePart.GetStream() retorna um fluxo não pesquisável para partes compactadas em pacotes ReadWrite

Quando você abre um Package com <System.IO.FileAccess.ReadWrite?displayProperty=nameWithType> e, em seguida, abre uma parte compactada para leitura que não foi modificada na sessão atual, PackagePart.GetStream agora retorna um fluxo somente de encaminhamento em vez de um buscavel MemoryStream.

Versão introduzida

.NET 11 Versão Prévia 7

Comportamento anterior

Anteriormente, quando você abriu um Package com FileAccess.ReadWrite (que é mapeado internamente para ZipArchiveMode.Update), abrir uma parte compactada para leitura retornou uma busca MemoryStream que continha o conteúdo de entrada totalmente descompactado.

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 retorna um fluxo somente de encaminhamento (não pesquisável) para partes compactadas 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 você observe essa alteração:

  • O pacote é aberto com FileAccess.ReadWrite (Package.Open(..., FileAccess.ReadWrite)).
  • A parte é aberta somente para leitura (GetStream(FileMode.Open, FileAccess.Read)).
  • A parte é compactada (CompressionOption diferente de NotCompressed).
  • A parte não foi escrita ou 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 somente leitura (FileAccess.Read): seus fluxos de partes compactadas já eram somente para encaminhamento.
  • Partes não compactadas (Stored): elas permanecem buscadas.
  • Partes modificadas anteriormente na sessão atual: elas são atendidas a partir de um instantâneo na memória, para que permaneçam buscadas.
  • Acessando Stream.Length.
  • Consumidores somente encaminhamento (o caso comum, como XmlReader, XDocument.Loado SDK do Open XML e CopyTo).
  • Estruturas de destino anteriores a .NET 11: a otimização é fechada atrásNET11_0_OR_GREATER.

Tipo de mudança disruptiva

Esta é uma alteração comportamental.

Motivo da alteração

Anteriormente, abrir uma parte compactada para leitura de um ReadWrite pacote sempre descompactou toda a entrada em um MemoryStream antes de retornar o fluxo. Essa abordagem produziu um fluxo pesquisável, mas impôs alocações de memória desnecessárias e sobrecarga de CPU para o caso comum em que os chamadores só leem o fluxo sequencialmente (por exemplo, XmlReader ou o SDK do Open XML). O novo comportamento é transmitido diretamente da entrada de arquivo ZIP subjacente para leituras somente de encaminhamento, o que evita a descompactação inicial.

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

Se o código exigir um fluxo buscavel de uma parte compactada 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.

Como alternativa, se o caso de uso não exigir ReadWrite acesso ao pacote, abra-o em FileAccess.Read vez disso. Os pacotes somente leitura já retornavam fluxos somente de encaminhamento antes dessa alteração, portanto, esse modo evita qualquer surpresa para código sensível à busca.

APIs afetadas