1
votes

I need to provide a WebView with content from a locally stored zip file.

I use the NavigateToLocalStreamUri()method and supply my own extended UriResolver in the same way as the Example found on the windows library link to NavigateToLocalStreamUri() .

However, debugging fails rather spectacularly as the "NTLSUri" method does not recognize the type of the stream and tries to cast it. Does anyone know how to correctly provide a Stream from a zipped archive element to a WebView?

Here are the details:

This is my own customised GetContent() method to provide a stream to the archive element rather than a normal.

private async Task<IInputStream> GetNormalZipEntryStream(String archivePath)
    {
        StorageFolder current = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync("bookTest");

        bookSourceFile = await current.GetItemAsync("testBook.zip") as StorageFile;

        if (archivePath.ElementAt(0) == '/') archivePath = archivePath.Remove(0, 1);

        Stream bookZipStream = await bookSourceFile.OpenStreamForReadAsync();
        ZipArchive bookArchive = new ZipArchive(bookZipStream, ZipArchiveMode.Read);
        ZipArchiveEntry bookContentFile = bookArchive.GetEntry(archivePath);

        if (bookContentFile == null)
            throw new Exception("Invalid archive entry");
        else
        {
            try
            {
                IInputStream stream = bookContentFile.Open().AsInputStream();
                return stream;
            }
            catch (Exception ex)
            {
                var streamConvErr = ex.Message;
            }
        }
        return null;
    }

Now, everything works fine, I'm able to successfully get a stream to the archive element. I've also confirmed that the ArchiveElement.Open() method does indeed return a stream with "uncompressed" content.

The problem is that - behind the scenes - the NAvigateToStreamUri() method is unable to accept the IInput stream returned by bookContentFile.Open().AsInputStream().

It causes a break in the debugger at the following code section:

#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
        UnhandledException += (sender, e) =>
        {
            if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
        };

With e (the exception) stating "Invalid Cast Operation" at this point.

Also note that, during debugging, at the point where AsInputStream() is called, stream's type is not as one would expect: of type IInputStream, rather it is of type System.IO.NetFxToWinRtStreamAdapter.InputStream

Expanding the object further sees the non public member "ManagedStream" as type System.IO.Compression.DeflateStream

I assume there are ways of using DataReader to convert the stream to a memory stream and then converting back to IInputstream, but this seems counter-intuitive as the provided AsInpuSream() method should work as expected.

2

2 Answers

1
votes

There seems to be no way ...as far as I can tell... to get around the IInputStream() returning a stream of Type: System.IO.NetFxToWinRtStreamAdapter.InputStream

I wrote the following method to manually copy the bytes into a new stream, using only the Windows.Storage.Stream types. The WebView has no complaints and everything works fine

private async Task<IInputStream> GetInputStreamFromIOStream(System.IO.Stream stream, long fileSize)
    {
        try
        {
            BinaryReader binReader = new BinaryReader(stream);
            byte[] byteArr = binReader.ReadBytes((int)fileSize);

            var iMS = new InMemoryRandomAccessStream();
            var imsOutputStream = iMS.GetOutputStreamAt(0);
            DataWriter dataWriter = new DataWriter(imsOutputStream);
            dataWriter.WriteBytes(byteArr);

            await dataWriter.StoreAsync();
            await imsOutputStream.FlushAsync();

            return iMS.GetInputStreamAt(0);
        }
        catch(Exception ex)
        {
            //Error Handling here
            return null;
        }
    }

Implemented in the following way for those interested:

        sourceFile = await current.GetItemAsync("zipfileHere.zip") as StorageFile;
        Stream zipStream = await sourceFile.OpenStreamForReadAsync();
        ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Read);
        ZipArchiveEntry contentFile = archive.GetEntry(archivePathVar);

        if (contentFile == null)
            throw new Exception("Invalid archive entry");
        else
        {
            try
            {
                var zipASize = contentFile.Length;
                IInputStream stream = await GetInputStreamFromIOStream(contentFile.Open(), zipASize);

                return stream;
            }
            catch (Exception ex)
            {
                //Some error handling here
            }

        }
0
votes

Try this:

try
{
    IInputStream stream = bookContentFile.Open().AsRandomAccessStream().GetInputStreamAt(0);
    return stream;
}
catch (Exception ex)
{
    var streamConvErr = ex.Message;
}

Basically, this converts the stream to the Windows Runtime equivalent first, then uses the built-in methods for getting the IInputStream version.