Compression in Windows Store Apps

Introduction

Today we are going to learn how to create Windows Store Apps for compression and decompression using JavaScript. This sample demonstrates how to serialize and compress JavaScript objects to an in-memory stream. And how to decompress and deserialize JavaScript objects from the in-memory stream.

I assume you can create a simple Windows Store App using JavaScript; for more help visit Simple Windows Store Apps using JavaScript.

Compression Algorithms:

  1. DEFAULT: No compression algorithms explicitly specified. Xpress is used by default.
  2. XPRESS: Compression ratio is fair. The fastest compression and decompression speeds. Low memory requirement.
  3. MSZIP: Compression ratio is high. Normal compression speed and fast decompression speed. Low memory requirement.


To start the creation of the apps, add three JavaScript pages by right-clicking on the js folder in the Solution Explorer and select Add > new item > JavaScript Page and then give an appropriate name. In the same way, add one HTML page to your project.

compression-Windows-Store _apps.jpg

Write the following code in default.html:

(function () {

    "use strict";

    var appTitle = "Compression/Decompression";

    var page = [

        { url: "page.html" }

    ];

 

    function activated(eventObject) {

        if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {

            eventObject.setPromise(WinJS.UI.processAll().then(function () {

                var url = WinJS.Application.sessionState.lastUrl || page[0].url;

                return WinJS.Navigation.navigate(url);

            }));

        }

    }

    WinJS.Navigation.addEventListener("navigated", function (eventObject) {

        var url = eventObject.detail.location;

        var host = document.getElementById("contentHost");

        host.winControl && host.winControl.unload && host.winControl.unload();

        WinJS.Utilities.empty(host);

        eventObject.detail.setPromise(WinJS.UI.Pages.render(url, host, eventObject.detail.state).then(function () {

            WinJS.Application.sessionState.lastUrl = url;

        }));

    });

    WinJS.Namespace.define("App", {

        appTitle: appTitle,

        pages: pages

    });

    WinJS.Application.addEventListener("activated", activated, false);

    WinJS.Application.start();

})();

Write the following code in default.js:
 

(function () {

    "use strict";

    var appTitle = "Compression/Decompression";

    var page = [

        { url: "page.html" }

    ];

 

    function activated(eventObject) {

        if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {

            eventObject.setPromise(WinJS.UI.processAll().then(function () {

                var url = WinJS.Application.sessionState.lastUrl || page[0].url;

                return WinJS.Navigation.navigate(url);

            }));

        }

    }

    WinJS.Navigation.addEventListener("navigated", function (eventObject) {

        var url = eventObject.detail.location;

        var host = document.getElementById("contentHost");

        host.winControl && host.winControl.unload && host.winControl.unload();

        WinJS.Utilities.empty(host);

        eventObject.detail.setPromise(WinJS.UI.Pages.render(url, host, eventObject.detail.state).then(function () {

            WinJS.Application.sessionState.lastUrl = url;

        }));

    });

    WinJS.Namespace.define("App", {

        appTitle: appTitle,

        page: page

    });

    WinJS.Application.addEventListener("activated", activated, false);

    WinJS.Application.start();

})();


Write the following code in page.html:
 

<DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

    <title></title>

    <link rel="stylesheet" href="/css/object.css" />

    <script src="/js/scriptcompress.js"></script>

    <script src="/js/script.js"></script>

</head>

<body>

    <div data-win-control="App.\Input">

        <button id="DefaultButton">DEFAULT (XPRESS)</button>

        <button id="XpressButton">XPRESS</button>

        <button id="MszipButton">MSZIP</button>

        <br />

        <br />

    </div>

    <div data-win-control="App.\Output">

        <div id="pageProgress" />

    </div>

</body>

</html>


Write the following code in script.js:
 

(function () {

    "use strict";

    var page = WinJS.UI.Pages.define("page.html", {

        ready: function (element, options) {

            function page(algorithm) {

                return function () {

                    clearProgress();

                    if (typeof (algorithm) === "undefined") {

                        dopage();

                    } else {

                        dopage(Windows.Storage.Compression.CompressAlgorithm[algorithm]);

                    }

                };

            }

 

            document.getElementById("DefaultButton").addEventListener("click", page(), false);

            document.getElementById("XpressButton").addEventListener("click", page("xpress"), false);

            document.getElementById("MszipButton").addEventListener("click", page("mszip"), false);

        }

    });

 

    function onError(e) {

        WinJS.log && WinJS.log(e, "app", "error");

    }

 

    function clearProgress() {

        WinJS.log && WinJS.log("Testing...", "app", "status");

 

        var progress = document.getElementById("pageProgress");

        progress.innerHTML = "";

    }

 

    function showProgress(message) {

        var progress = document.getElementById("pageProgress");

        progress.innerHTML += "<p>" + message + "</p>";

    }

 

    function getEnumerationValueName(namespace, value, defaultName) {

        for (var name in namespace) {

            if (namespace[name] === value) {

                return name;

            }

        }

 

        return defaultName;

    }

 

 

    function dopage(compressAlgorithm) {

        var testObject = [1, 2, 3, "apptext", { value: "appobject" }];

 

        try {

            var stream = new Windows.Storage.Streams.InMemoryRandomAccessStream();

            var compressor;

            if (typeof (compressAlgorithm) === "undefined") {

                compressor = new WinJS.Compressor(stream.getOutputStreamAt(0));

            } else {

                compressor = new WinJS.Compressor(stream.getOutputStreamAt(0), compressAlgorithm);

            }

 

            var decompressor = new WinJS.Decompressor(stream.getInputStreamAt(0));

            showProgress("Compressor object created");

            compressor.compressAsync(testObject).then(function () {

                showProgress("Test object compressed");

                compressor.close();

                return decompressor.readObjectAsync();

            }, onError).then(function (decompressedObject) {

                decompressor.close();

                showProgress("Test object decompressed: " + decompressedObject);

                if (JSON.stringify(testObject) === JSON.stringify(decompressedObject)) {

                    showProgress("Test object matches decompressed one");

                } else {

                    onError(new Error("Test object doesn't match decompressed one"));

                }

            }, onError).done(function () {

                WinJS.log && WinJS.log("Test finished", "app", "status");

            }, onError);

 

        } catch (e) {

            onError(e);

        }

    }

})(); 


Write the following code in script1.js:
 

(function () {

    var pageOutput = WinJS.Class.define(

         function (element, options) {

             element.winControl = this;

             this.element = element;

             new WinJS.Utilities.QueryCollection(element)

                         .setAttribute("role", "region")

                         .setAttribute("aria-labelledby", "outputLabel")

                         .setAttribute("aria-live", "assertive");

             element.id = "output";

 

             this._addOutputLabel(element);

             this._addStatusOutput(element);

         }, {

             _addOutputLabel: function (element) {

                 var label = document.createElement("h2");

                 label.id = "outputLabel";

                 label.textContent = "Output";

                 element.parentNode.insertBefore(label, element);

             },

             _addStatusOutput: function (element) {

                 var statusDiv = document.createElement("div");

                 statusDiv.id = "statusMessage";

                 element.insertBefore(statusDiv, element.childNodes[0]);

             }

         }

     );

    var currentUrl = null;

 

    WinJS.Navigation.addEventListener("navigating", function (evt) {

        currentUrl = evt.detail.location;

    });

 

    WinJS.log = function (message, tag, type) {

        var statusDiv = document.getElementById("statusMessage");

    };

    function activated(e) {

        WinJS.Utilities.query("#featureLabel")[0].textContent = App.appTitle;

    }

 

    WinJS.Application.addEventListener("activated", activated, false);

    WinJS.Namespace.define("App", {

        pageOutput: pageOutput

    });

})(); 


Write the following code in scriptcompress.js:
 

 

(function (global, WinJS) {

    function getUnderlyingStreamPromise(underlyingStream, that) {

        if (typeof (underlyingStream) === "string" || underlyingStream instanceof String) {

            var docs = Windows.Storage.KnownFolders.documentsLibrary;

            return docs.createFileAsync(underlyingStream).then(function (file) {

                that._storageFile = file;

                return file.openAsync(Windows.Storage.FileAccessMode.readWrite);

            }).then(function (randomAccessStream) {

                that._ownedStream = randomAccessStream;

                return that._ownedStream;

            });

        } else if (typeof (underlyingStream.writeAsync) === "function") {

            return WinJS.Promise.as(underlyingStream);

        } else if (typeof (underlyingStream.openAsync) === "function") {

            that._storageFile = underlyingStream;

            return underlyingStream.openAsync(Windows.Storage.FileAccessMode.readWrite).then(function (randomAccessStream) {

                that._ownedStream = randomAccessStream;

                return that._ownedStream;

            });

        } else {

            throw new Error("Cannot create IOutputStream from underlyingStream object");

        }

    }

 

    var Compressor = WinJS.Class.define(

        function (underlyingStream, compressAlgorithm) {

            this._underlyingStreamPromise = getUnderlyingStreamPromise(underlyingStream, this);

            this._compressAlgorithm = compressAlgorithm ? compressAlgorithm : Windows.Storage.Compression.CompressAlgorithm.xpress;

        },

        {

            compressAsync: function (data) {

                var that = this;

 

                if (!that._underlyingStreamPromise) {

                    return WinJS.Promise.wrapError(new Error("Object has already been closed"));

                }

 

                return new WinJS.Promise(function (complete, error) {

                    that._underlyingStreamPromise.then(function (underlyingStream) {

                        var compressor = new Windows.Storage.Compression.Compressor(underlyingStream, that._compressAlgorithm, /* use default block size */0);

                        var writer = new Windows.Storage.Streams.DataWriter(compressor);

 

                        function finalize() {

                            compressor.finishAsync().then(function () {

                                compressor.detachStream();

                                compressor.close();

                                underlyingStream.flushAsync().done(complete, error);

                            }, error);

                        }

 

                        if (typeof (data.readAsync) === "function") {

                            Windows.Storage.Stream.RandomAccessStream.copyAsync(data, compressor).done(finalize, error);

                        } else if (typeof (data.openAsync) === "function") {

                            data.openAsync(Windows.Storage.FileAccessMode.read).done(function (randomAccessStream) {

                                return Windows.Storage.Streams.RandomAccessStream.copyAsync(randomAccessStream, compressor).then(function () {

                                    randomAccessStream.close();

                                    finalize();

                                }, error);

                            }, error);

                        } else if (typeof (data) === "string" || data instanceof String) {

                            writer.writeString(data);

                            writer.storeAsync().done(finalize, error);

                        } else if (typeof (data) === "object") {

                            writer.writeString(JSON.stringify(data));

                            writer.storeAsync().done(finalize, error);

                        } else {

                            error(new Error("Invalid input data"));

                        }

                    }, error);

                });

            },

            fileName: {

                get: function () {

                    if (this._storageFile) {

                        return this._storageFile.name;

                    } else {

                        return null;

                    }

                }

            },

 

            close: function () {

                if (this._ownedStream) {

                    this._ownedStream.close();

                    this._ownedStream = null;

                }

                this._underlyingStreamPromise = null;

            }

        }

    );

 

    WinJS.Namespace.define("WinJS", {

        Compressor: Compressor

    });

})(this, WinJS);

(function (global, WinJS) {

    function getUnderlyingStreamPromise(underlyingStream, that) {

        if (typeof (underlyingStream) === "string" || underlyingStream instanceof String) {

            var docs = Windows.Storage.KnownFolders.documentsLibrary;

            return docs.getFileAsync(underlyingStream).then(function (file) {

                return file.openAsync(Windows.Storage.FileAccessMode.read);

            }).then(function (randomAccessStream) {

                that._ownedStream = randomAccessStream;

                return that._ownedStream;

            });

        } else if (typeof (underlyingStream.readAsync) === "function") {

            return WinJS.Promise.as(underlyingStream);

        } else if (typeof (underlyingStream.openAsync) === "function") {

            return underlyingStream.openAsync(Windows.Storage.FileAccessMode.read).then(function (randomAccessStream) {

                that._ownedStream = randomAccessStream;

                return that._ownedStream;

            });

        } else {

            throw new Error("Cannot create IInputStream from underlyingStream object");

        }

    }

 

    var Decompressor = WinJS.Class.define(

        function (underlyingStream) {

            this._underlyingStreamPromise = getUnderlyingStreamPromise(underlyingStream, this);

        },

        {

            readStringAsync: function () {

                var that = this;

 

                if (!that._underlyingStreamPromise) {

                    return WinJS.Promise.wrapError(new Error("Object has already been closed"));

                }

 

                return new WinJS.Promise(function (complete, error) {

                    that._underlyingStreamPromise.then(function (underlyingStream) {

                        var decompressor = new Windows.Storage.Compression.Decompressor(underlyingStream);

                        var reader = new Windows.Storage.Streams.DataReader(decompressor);

 

                        var result = "";

                        function readStream(chunkLength) {

                            reader.loadAsync(chunkLength).done(function (bytesRead) {

                                if (bytesRead === 0) {

                                    decompressor.detachStream();

                                    decompressor.close();

                                    complete(result);

                                    return;

                                }

 

                                result += reader.readString(bytesRead);

                                readStream(chunkLength);

                            }, error);

                        }

 

                        readStream(16384);

                    }, error);

                });

            },

 

            readObjectAsync: function (reviver) {

                var that = this;

                return new WinJS.Promise(function (complete, error) {

                    that.readStringAsync().then(function (str) {

                        try {

                            complete(JSON.parse(str, reviver));

                        } catch (e) {

                            error(e);

                        }

                    }, error);

                });

            },

 

            copyAsync: function (destinationStream) {

                var that = this;

 

                if (!that._underlyingStreamPromise) {

                    return WinJS.Promise.wrapError(new Error("Object has already been closed"));

                }

 

                return new WinJS.Promise(function (complete, error) {

                    that._underlyingStreamPromise.then(function (underlyingStream) {

                        var decompressor = new Windows.Storage.Compression.Decompressor(underlyingStream);

                        var openedStream = null;

 

                        function finalize() {

                            decompressor.detachStream();

                            decompressor.close();

                            if (openedStream) {

                                openedStream.flushAsync().then(function () {

                                    openedStream.close();

                                    complete();

                                }, error);

                            } else {

                                complete();

                            }

                        }

 

                        if (typeof (destinationStream) === "string" || destinationStream instanceof String) {

                            var docs = Windows.Storage.KnownFolders.documentsLibrary;

                            docs.createFileAsync(destinationStream).then(function (file) {

                                return file.openAsync(Windows.Storage.FileAccessMode.readWrite);

                            }).then(function (randomAccessStream) {

                                openedStream = randomAccessStream;

                                Windows.Storage.Streams.RandomAccessStream.copyAsync(decompressor, randomAccessStream).done(finalize, error);

                            }, error);

                        } else if (typeof (destinationStream.writeAsync) === "function") {

                            Windows.Storage.Stream.RandomAccessStream.copyAsync(decompressor, destinationStream).done(finalize, error);

                        } else if (typeof (data.openAsync) === "function") {

                            data.openAsync(Windows.Storage.FileAccessMode.readWrite).then(function (randomAccessStream) {

                                openedStream = randomAccessStream;

                                Windows.Storage.Streams.RandomAccessStream.copyAsync(decompressor, randomAccessStream).done(finalize, error);

                            }, error);

                        } else {

                            error(new Error("Invalid destination stream"));

                        }

                    }, error);

                });

            },

 

            close: function () {

                if (this._ownedStream) {

                    this._ownedStream.close();

                    this._ownedStream = null;

                }

                this._underlyingStreamPromise = null;

            }

        }

    );

 

    WinJS.Namespace.define("WinJS", {

        Decompressor: Decompressor

    });

})(this, WinJS); 


Output:


compress-Windows-store-Apps.jpg

Summary

In this article I described how to create a Windows Store App for compression and decompression using JavaScript. I hope this article has helped you in understanding this topic. Please share it. If you know more about this, your feedback and constructive contributions are welcome.