PackagePart.GetStream() returnerar en dataström som inte kan sökas efter komprimerade delar i ReadWrite-paket

När du öppnar en Package med <System.IO.FileAccess.ReadWrite?displayProperty=nameWithType> och sedan öppnar en komprimerad del för läsning som inte har ändrats i den aktuella sessionen, PackagePart.GetStream returnerar nu en dataström endast framåt i stället för en sökbar MemoryStream.

Version lanserad

.NET 11 Förhandsversion 7

Tidigare beteende

Tidigare, när du öppnade en Package med FileAccess.ReadWrite (som mappar internt till ZipArchiveMode.Update), returnerade öppnandet av en komprimerad del för läsning en sökbar MemoryStream som innehöll det fullständigt dekomprimerade inmatningsinnehållet.

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

Nytt beteende

Från och med .NET 11 returnerar samma anrop en ström med endast vidarebefordran (ej sökbar) för komprimerade delar som inte har ändrats i den aktuella sessionen.

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)

Alla följande villkor måste vara uppfyllda samtidigt för att du ska kunna observera den här ändringen:

  • Paketet öppnas med FileAccess.ReadWrite (Package.Open(..., FileAccess.ReadWrite)).
  • Delen öppnas endast för läsning (GetStream(FileMode.Open, FileAccess.Read)).
  • Delen komprimeras (CompressionOption förutom NotCompressed).
  • Delen skrevs eller ändrades inte tidigare i samma session.
  • Konsumenten söker villkorslöst dataströmmen eller läser Position.

Följande scenarier påverkas inte:

  • Skrivskyddade paket (FileAccess.Read): Deras komprimerade delströmmar var redan framåtriktade.
  • Okomprimerade (Stored) delar: De är fortfarande sökbara.
  • Delar som ändrades tidigare i den aktuella sessionen: De hanteras från en minnesintern ögonblicksbild, så de är fortfarande sökbara.
  • Åtkomst till Stream.Length.
  • Konsumenter med endast vidarebefordran (det vanliga fallet, till exempel XmlReader, XDocument.Load, Open XML SDK och CopyTo).
  • Målramverk tidigare än .NET 11: Optimeringen är gated bakom NET11_0_OR_GREATER.

Typ av brytande ändring

Den här ändringen är en beteendeförändring.

Orsak till ändringen

Tidigare, öppna en komprimerad del för läsning från ett ReadWrite paket alltid dekomprimerade hela posten i en MemoryStream innan den returnerade strömmen. Den här metoden skapade en sökbar ström, men det medförde onödiga minnesallokeringar och processorkostnader för det vanliga fallet där anropare bara läste strömmen sekventiellt (till exempel XmlReader eller Open XML SDK). Det nya beteendet strömmar direkt från den underliggande ZIP-arkivposten för framåtriktade läsningar, vilket undviker dekomprimering i förväg.

Mer information finns i dotnet/runtime#129698.

Om koden kräver en sökbar ström från en komprimerad del av ett ReadWrite paket kopierar du den till en MemoryStream manuellt:

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.

Om ditt användningsfall inte kräver ReadWrite åtkomst till paketet kan du också öppna det med FileAccess.Read i stället. Skrivskyddade paket returnerade redan framåtriktade strömmar före den här ändringen, så det här läget undviker överraskningar för sökkänslig kod.

Berörda API:er