PackagePart.GetStream() retourneert een niet-zoekbare stream voor gecomprimeerde onderdelen in ReadWrite-pakketten

Wanneer u een Package bestand opent met <System.IO.FileAccess.ReadWrite?displayProperty=nameWithType> en vervolgens een gecomprimeerd deel opent voor het lezen dat niet is gewijzigd in de huidige sessie, PackagePart.GetStream retourneert nu een stroom met alleen-doorsturen in plaats van een zoekbaar MemoryStream.

Geïntroduceerde versie

.NET 11 preview 7

Vorig gedrag

Voorheen, toen u een Package met FileAccess.ReadWrite (die intern wordt toegewezen aan ZipArchiveMode.Update), opent u een gecomprimeerd deel voor het lezen een zoekbaar MemoryStream item dat de volledig gedecomprimeerde invoerinhoud bevatte.

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

Nieuw gedrag

Vanaf .NET 11 retourneert dezelfde aanroep een stroom voor alleen-doorsturen (niet-zoekbaar) voor gecomprimeerde onderdelen die niet zijn gewijzigd in de huidige sessie.

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)

Alle volgende voorwaarden moeten tegelijkertijd waar zijn om deze wijziging te observeren:

  • Het pakket wordt geopend met FileAccess.ReadWrite (Package.Open(..., FileAccess.ReadWrite)).
  • Het onderdeel wordt geopend voor alleen-lezen (GetStream(FileMode.Open, FileAccess.Read)).
  • Het deel wordt gecomprimeerd (CompressionOption anders dan NotCompressed).
  • Het onderdeel is niet eerder in dezelfde sessie geschreven of gewijzigd.
  • De consument zoekt onvoorwaardelijke naar de stroom of leest Position.

De volgende scenario's worden niet beïnvloed:

  • Alleen-lezenpakketten (FileAccess.Read): de gecomprimeerde deelstreams waren al forward-only.
  • Niet-gecomprimeerde (Stored) delen: ze blijven zoekbaar.
  • Onderdelen die eerder in de huidige sessie zijn gewijzigd: ze worden geleverd vanuit een momentopname in het geheugen, zodat ze kunnen worden gezocht.
  • Toegang tot Stream.Length.
  • Gebruikers die alleen doorsturen (het algemene geval, zoals XmlReader, XDocument.Loadde Open XML SDK en CopyTo).
  • Doelframeworks ouder dan .NET 11: De optimalisatie wordt achter NET11_0_OR_GREATERgesloten.

Type van brekende verandering

Deze wijziging is een gedragswijziging.

Reden voor wijziging

Voorheen werd het openen van een gecomprimeerd deel voor het lezen van een ReadWrite pakket altijd gedecomprimeerd in een MemoryStream volledige vermelding voordat de stream werd geretourneerd. Deze benadering heeft een zoekbare stroom geproduceerd, maar er zijn onnodige geheugentoewijzingen en CPU-overhead voor het algemene geval waarin bellers de stroom alleen sequentieel lezen (bijvoorbeeld XmlReader de Open XML SDK). Het nieuwe gedrag streamt rechtstreeks vanuit de onderliggende ZIP-archiefvermelding voor alleen-doorstuur-leesbewerkingen, waardoor de decompressie vooraf wordt vermeden.

Zie dotnet/runtime#129698 voor meer informatie.

Als voor uw code een zoekbare stream van een gecomprimeerd deel van een ReadWrite pakket is vereist, kopieert u deze handmatig naar een 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.

Als uw use-case geen toegang tot het pakket vereist ReadWrite , opent u het in FileAccess.Read plaats daarvan. Alleen-lezenpakketten hebben al streams met het kenmerk Alleen-lezen geretourneerd vóór deze wijziging. Deze modus voorkomt dus verrassingen voor zoekgevoelige code.

Betreffende API's