[Mono-dev] Patch for ZipPackagePart.GetStreamCore in System.IO.Packaging (olive)
Yves Dhondt
yves.dhondt at gmail.com
Mon Mar 16 15:21:27 EDT 2009
Issue:
-------
ZipPackagePart.GetStreamCore in System.IO.Packaging does not handle
Filemode mode input parameter (correctly).
Test program (requires an existing openxml document, for example
test.docx as input):
-------------------
I'm not that good with unit testing, but the following code should be
easily translated into 4 tests.
using System;
using System.IO;
using System.IO.Packaging;
namespace MonoOpenXmlExperiment
{
class Program
{
static void Main(string[] args)
{
// Warning do this with a document which can be damaged!!!
Package pack = Package.Open(args[0]);
PackagePart part = pack.CreatePart(new Uri("/somepart.xml",
UriKind.Relative), "application/xml");
try
{
Stream s2 = part.GetStream(FileMode.CreateNew, FileAccess.Write);
}
catch (Exception ex)
{
// An ArgumentException should be throw as CreateNew is an
unsupported mode.
Console.WriteLine("Expected ArgumentException, got {0}.",
ex.GetType().ToString());
}
try
{
Stream s3 = part.GetStream(FileMode.Truncate, FileAccess.Write);
}
catch (Exception ex)
{
// An ArgumentException should be throw as Truncate is an
unsupported mode.
Console.WriteLine("Expected ArgumentException, got {0}.",
ex.GetType().ToString());
}
try
{
Stream s4 = part.GetStream(FileMode.Append, FileAccess.Write);
}
catch (Exception ex)
{
// An ArgumentException should be throw as Append is an
unsupported mode.
Console.WriteLine("Expected ArgumentException, got {0}.",
ex.GetType().ToString());
}
Stream s = part.GetStream(FileMode.OpenOrCreate, FileAccess.Write);
StreamWriter sw = new StreamWriter(s);
sw.Write("<test>aaaaaaa</test>");
sw.Flush();
Stream s5 = part.GetStream(FileMode.Create, FileAccess.ReadWrite);
StreamWriter sw2 = new StreamWriter(s5);
sw2.Write("<test>bbb</test>");
sw2.Flush();
// Verify that the part got overwritten correctly.
Stream s6 = part.GetStream();
StreamReader sr = new StreamReader(s6);
Console.WriteLine("Expected <test>bbb</test>, got {0}.", sr.ReadToEnd());
pack.Close();
}
}
}
Expected output:
------------------------
Expected ArgumentException, got System.ArgumentException.
Expected ArgumentException, got System.ArgumentException.
Expected ArgumentException, got System.ArgumentException.
Expected <test>bbb</test>, got <test>bbb</test>.
Output currently given by mono:
---------------------------------------------
Expected <test>bbb</test>, got <test>bbb</test>.
Remarks:
-------------
The last test is especially important as it indicates that a create
would only overwrite part of stream.
Solution:
------------
Replace function by:
protected override Stream GetStreamCore (FileMode mode, FileAccess access)
{
MemoryStream stream;
switch (mode)
{
case FileMode.CreateNew:
throw new ArgumentException("FileMode not supported", "CreateNew");
case FileMode.Truncate:
throw new ArgumentException("FileMode not supported", "Truncate");
case FileMode.Append:
throw new ArgumentException("FileMode not supported", "Append");
case FileMode.Open:
if (Package.PartStreams.TryGetValue(Uri, out stream)) {
return new ZipPartStream(Package, stream, access);
}
else {
throw new IOException(); // Verify that this is the
correct exception.
}
case FileMode.OpenOrCreate:
case FileMode.Create:
// Check if a stream is already loaded. If not, load it or
create an empty one.
if (!Package.PartStreams.TryGetValue(Uri, out stream)) {
stream = new MemoryStream();
try
{
using (UnzipArchive archive = new
UnzipArchive(Package.PackageStream))
{
foreach (string file in archive.GetFiles())
{
if (file != Uri.ToString().Substring(1))
continue;
using (Stream archiveStream = archive.GetStream(file))
{
int read = 0;
byte[] buffer = new
byte[Math.Min(archiveStream.Length, 2 * 1024)];
while ((read = archiveStream.Read(buffer, 0,
buffer.Length)) != 0)
stream.Write(buffer, 0, read);
}
}
}
}
catch
{
// The zipfile is invalid, so just create the file
// as if it didn't exist
stream.SetLength(0);
}
Package.PartStreams.Add(Uri, stream);
}
// This ensures that an existing stream to which a create
// is applied will be overwritten.
if (mode == FileMode.Create)
stream.SetLength(0);
return new ZipPartStream(Package, stream, access);
default:
throw new ArgumentOutOfRangeException("mode");
}
}
More information about the Mono-devel-list
mailing list