Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Cuando se abre con Package<System.IO.FileAccess.ReadWrite?displayProperty=nameWithType> y, a continuación, se abre una parte comprimida para leer que no se ha modificado en la sesión actual, PackagePart.GetStream ahora devuelve una secuencia de solo avance en lugar de un objeto que se puede MemoryStreambuscar.
Versión introducida
.NET 11 Preview 7
Comportamiento anterior
Anteriormente, al abrir un Package objeto con FileAccess.ReadWrite (que se asigna internamente a ZipArchiveMode.Update), al abrir una parte comprimida para leer, se devolvió una búsqueda MemoryStream que contenía el contenido de 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
Nuevo comportamiento
A partir de .NET 11, la misma llamada devuelve una secuencia de solo avance (no buscable) para las partes comprimidas que no se han modificado en la sesión actual.
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 las condiciones siguientes deben ser verdaderas simultáneamente para que observe este cambio:
- El paquete se abre con
FileAccess.ReadWrite(Package.Open(..., FileAccess.ReadWrite)). - La parte se abre solo para lectura (
GetStream(FileMode.Open, FileAccess.Read)). - El elemento se comprime (
CompressionOptiondistinto deNotCompressed). - La parte no se escribió ni modificó anteriormente en la misma sesión.
- El consumidor busca incondicionalmente la secuencia o lee
Position.
Los escenarios siguientes no se ven afectados:
- Paquetes de solo lectura (
FileAccess.Read): sus secuencias de elementos comprimidas ya eran de solo avance. - Partes sin comprimir (
Stored): permanecen buscables. - Partes modificadas anteriormente en la sesión actual: se sirven desde una instantánea en memoria, por lo que permanecen buscables.
- Acceso a
Stream.Length. - Consumidores de solo reenvío (el caso común, como
XmlReader,XDocument.Load, el SDK de Open XML yCopyTo). - Marcos de destino anteriores a .NET 11: la optimización se encuentra detrás
NET11_0_OR_GREATERde .
Tipo de cambio disruptivo
Este es un cambio de comportamiento.
Motivo del cambio
Anteriormente, al abrir una parte comprimida para leer desde un ReadWrite paquete siempre se descomprimía toda la entrada en un MemoryStream antes de devolver la secuencia. Este enfoque generó una secuencia que se puede buscar, pero impedía asignaciones de memoria innecesarias y sobrecarga de CPU para el caso común en el que los autores de llamadas solo leen secuencialmente la secuencia (por ejemplo, XmlReader o el SDK de Open XML). El nuevo comportamiento transmite directamente desde la entrada de archivo ZIP subyacente para lecturas de solo avance, lo que evita la descompresión inicial.
Para obtener más información, vea dotnet/runtime#129698.
Acción recomendada
Si el código requiere una secuencia que se puede buscar desde una parte comprimida de un ReadWrite paquete, cópiela en un elemento MemoryStream manualmente:
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, si el caso de uso no requiere ReadWrite acceso al paquete, ábralo con FileAccess.Read en su lugar. Los paquetes de solo lectura ya devolvieron secuencias de solo avance antes de este cambio, por lo que este modo evita cualquier sorpresa para el código sensible a la búsqueda.