nsIZipWriter

This interface provides an easy way for scripts to archive data in the Zip file format. Operations on the archive can be performed one by one, or queued for later execution.
1.0
66
Introduced
Gecko 1.9
Inherits from: nsISupports Last changed in Gecko 1.9 (Firefox 3)

Once all the operations you wish to perform are added to the queue, a call to processQueue() will perform the operations in the order they were added to the queue. Operations performed on the queue throw any errors that occur out to the observer.

Attempting to perform a synchronous operation on the interface while the background queue is in progress will throw an NS_ERROR_IN_PROGRESS exception.

File and directory names should use slashes ("/") as separators and should not begin with a slash.

Note: Although it is not necessary to add directory entries in order to add file entries within them, some Zip utilities may have problems with that, so it may be best to add the directory entries explicitly first.

Implemented by: @mozilla.org/zipwriter;1. To create an instance, use:

var zipWriter = Components.classes["@mozilla.org/zipwriter;1"]
                .createInstance(Components.interfaces.nsIZipWriter);

Method overview

void addEntryChannel(in AUTF8String aZipEntry, in PRTime aModTime, in PRInt32 aCompression, in nsIChannel aChannel, in boolean aQueue);
void addEntryDirectory(in AUTF8String aZipEntry, in PRTime aModTime, in boolean aQueue);
void addEntryFile(in AUTF8String aZipEntry, in PRInt32 aCompression, in nsIFile aFile, in boolean aQueue);
void addEntryStream(in AUTF8String aZipEntry, in PRTime aModTime, in PRInt32 aCompression, in nsIInputStream aStream, in boolean aQueue);
void close();
nsIZipEntry getEntry(in AUTF8String aZipEntry);
boolean hasEntry(in AUTF8String aZipEntry);
void open(in nsIFile aFile, in PRInt32 aIoFlags);
void processQueue(in nsIRequestObserver aObserver, in nsISupports aContext);
void removeEntry(in AUTF8String aZipEntry, in boolean aQueue);

Attributes

Attribute Type Description
comment ACString Gets or sets the comment associated with the currently open Zip file.
Exceptions thrown
NS_ERROR_NOT_INITIALIZED
If no zip file has been opened.
file nsIFile The Zip file being written to. Read only.
inQueue boolean true if operations are being performed in the background queue, or false if background operations are not in progress. Read only.

Constants

Constant Value Description
COMPRESSION_NONE 0 Do not compress the file.
COMPRESSION_FASTEST 1 Use the fastest compression method when adding the file to the archive.
COMPRESSION_DEFAULT 6 Use the default compression method when adding the file to the archive.
COMPRESSION_BEST 9 Use the best compression method when adding the file to the archive.

Methods

addEntryChannel()

Adds data from a channel to the Zip file. If the operation is performed on the queue then the channel will be opened asynchronously, otherwise the channel must support being opened synchronously.

void addEntryChannel(
  in AUTF8String aZipEntry,
  in PRTime aModTime,
  in PRInt32 aCompression,
  in nsIChannel aChannel,
  in boolean aQueue
);
Parameters
aZipEntry
The path of the file entry to add to the Zip file. This is the path it will be located at within the Zip file.

Note: You must use forward slashes ('/') in the path.

aModTime
The modification time of the entry in microseconds.
aCompression
One of the compression constants, indicating the compression method to use.
aChannel
The channel from which to get the data.
aQueue
If true, the operation is queued for later execution. If false, the operation is performed immediately. Will be performed when processQueue() is called.
Exceptions thrown
NS_ERROR_NOT_INITIALIZED
No Zip file is open.
NS_ERROR_FILE_ALREADY_EXISTS
The specified path already exists in the Zip file.
NS_ERROR_IN_PROGRESS
The Zip writer is already performing another operation.

addEntryDirectory()

Adds a new directory entry to the Zip file. If aZipEntry does not end with "/" then it will be added.

Note: Although it is not necessary to add directory entries in order to add file entries within them, some Zip utilities may have problems with that, so it may be best to add the directory entries explicitly first.

void addEntryDirectory(
  in AUTF8String aZipEntry,
  in PRTime aModTime,
  in boolean aQueue
);
Parameters
aZipEntry
The path of the directory entry to add to the Zip file.
Note: You must use forward slashes in the path. (A forward slash is '/')

Detail: This aZipEntry is very important, this example demonstrates its usage:

var zw = Cc['@mozilla.org/zipwriter;1'].createInstance(Ci.nsIZipWriter);
var myZipFile = fu.File('C:\\MyZipFile.zip'); //this file will be creatd in C drive
var pr = {PR_RDONLY: 0x01, PR_WRONLY: 0x02, PR_RDWR: 0x04, PR_CREATE_FILE: 0x08, PR_APPEND: 0x10, PR_TRUNCATE: 0x20, PR_SYNC: 0x40, PR_EXCL: 0x80};
zw.open(xpi, pr.PR_WRONLY | pr.PR_CREATE_FILE | pr.PR_TRUNCATE); //xpi file is created if not there, if it is there it is truncated/deleted

var fileToAddToZip = FileUtils.File('C:\\add this file.txt');
var saveInZipAs = 'blah.txt';
zw.addEntryFile(saveInZipAs, Ci.nsIZipWriter.COMPRESSION_NONE, fileToAddToZip, false);

In the above example, the file ("add this file.txt") located at "C:\add this file.txt" will be added to the zip file "C:\MyZipFile.zip", however in the zip file this file ("add this file.txt") will be named "blah.txt". If saveInZipAs was equal to "add this file.txt" then in the zip file it will also be named "add this file.txt"

Further Detail: This example gives futher detail on the importance of forward slashes

var fileToAddToZip = FileUtils.File('C:\\add this file.txt');
var saveInZipAs = 'sub folder/blah.txt'; //DO NOT USE BACKWARD SLASH for example do not do: 'sub folder\\blah.txt'. Using backward slash will not throw any errors but if this was an xpi file, firefox would not be able to read it properly.
zw.addEntryFile(saveInZipAs, Ci.nsIZipWriter.COMPRESSION_NONE, fileToAddToZip, false);

This example creates a folder in the zip called "sub folder" and then adds "C:\add this file.txt" to it, but it will be called "blah.txt".

aModTime
The modification time of the entry in microseconds.
aQueue
If true, the operation is queued for later execution. If false, the operation is performed immediately. Will be performed when processQueue() is called.
Exceptions thrown
NS_ERROR_NOT_INITIALIZED
No Zip file is open.
NS_ERROR_FILE_ALREADY_EXISTS
The specified path already exists in the Zip file.
NS_ERROR_IN_PROGRESS
The Zip writer is already performing another operation.

addEntryFile()

Adds a new file or directory to the Zip file. If the specified file is a directory, this call is equivalent to:

addEntryDirectory(aZipEntry, aFile.lastModifiedTime, aQueue);
void addEntryFile(
  in AUTF8String aZipEntry,
  in PRInt32 aCompression,
  in nsIFile aFile,
  in boolean aQueue
);
Parameters
aZipEntry
The path of the file entry to add to the Zip file. This is the path it will be located at within the Zip file.
Note: You must use forward slashes in the path. (A forward slash is '/')
aCompression
One of the compression constants, indicating the compression method to use.
aFile
The file from which to get the data and modification time.
aQueue
If true, the operation is queued for later execution. If false, the operation is performed immediately. Will be performed when processQueue() is called.
Exceptions thrown
NS_ERROR_NOT_INITIALIZED
No Zip file is open.
NS_ERROR_FILE_ALREADY_EXISTS
The specified path already exists in the Zip file.
NS_ERROR_IN_PROGRESS
The Zip writer is already performing another operation.
NS_ERROR_FILE_NOT_FOUND
If file does not exist.

addEntryStream()

Adds data from an input stream to the Zip file.

void addEntryStream(
  in AUTF8String aZipEntry,
  in PRTime aModTime,
  in PRInt32 aCompression,
  in nsIInputStream aStream,
  in boolean aQueue
);
Parameters
aZipEntry
The path of the file entry to add to the Zip file. This is the path it will be located at within the Zip file.
Note: You must use forward slashes in the path.
aModTime
The modification time of the entry in microseconds.
aCompression
One of the compression constants, indicating the compression method to use.
aStream
The input stream from which to get the data.
aQueue
If true, the operation is queued for later execution. If false, the operation is performed immediately. Will be performed when processQueue() is called.
Exceptions thrown
NS_ERROR_NOT_INITIALIZED
No Zip file is open.
NS_ERROR_FILE_ALREADY_EXISTS
The specified path already exists in the Zip file.
NS_ERROR_IN_PROGRESS
The Zip writer is already performing another operation.

close()

Closes the Zip file.

void close();
Parameters

None.

Exceptions thrown
NS_ERROR_NOT_INITIALIZED
No Zip file is open.
NS_ERROR_IN_PROGRESS
The Zip writer is already performing another operation.

Other errors may be thrown if a failure to complete the Zip file occurs.

getEntry()

Returns a specified entry or null if there is no such entry in the current Zip file.

nsIZipEntry getEntry(
  in AUTF8String aZipEntry
);
Parameters
aZipEntry
The path of the file entry to get.
Return value

An nsIZipEntry object describing the specified entry, or null if there is no such entry.

hasEntry()

Determines whether or not a specific entry exists in the Zip file.

boolean hasEntry(
  in AUTF8String aZipEntry
);
Parameters
aZipEntry
The path of the file entry to check for.
Return value

true if there is an entry with the given path in the Zip file, otherwise returns false.

open()

Opens the specified Zip file.

void open(
  in nsIFile aFile,
  in PRInt32 aIoFlags
);
Parameters
aFile
The Zip file to open.
aIoFlags
The open flags for the Zip file, from prio.h.
Exceptions thrown
NS_ERROR_ALREADY_INITIALIZED
A Zip file is already open.
NS_ERROR_INVALID_ARG
The aFile parameter is null.
NS_ERROR_FILE_NOT_FOUND
The specified file was not found and the flags didn't permit creating it. Or the directory for the file does not exist.
NS_ERROR_FILE_CORRUPTED
The specified file is not a recognizable Zip file.

Other errors may be thrown upon failing to open the file, such as if the file is corrupt or in an unsupported format.

processQueue()

Processes all queued items until the entire queue has been processed or an error occurs. The observer is notified when the first operation begins and when the last operation completes.

Any failures are passed to the observer.

The Zip writer will be busy until the queue is complete or an error halts processing of the queue early. In the event of an early failure, the remaining items stay in the queue; calling processQueue() again will continue where operations left off.

void processQueue(
  in nsIRequestObserver aObserver,
  in nsISupports aContext
);
Parameters
aObserver
The observer to receive notifications from the queue.
aContext
The context to pass to the observer.
Exceptions thrown
NS_ERROR_NOT_INITIALIZED
No Zip file is open.
NS_ERROR_IN_PROGRESS
The queue is already in progress.

removeEntry()

Removes an entry from the Zip file.

void removeEntry(
  in AUTF8String aZipEntry,
  in boolean aQueue
);
Parameters
aZipEntry
The path of the entry to remove from the Zip file.
aQueue
true to place the remove operation into the queue, or false to process it at once. Will be performed when processQueue() is called.
Exceptions thrown
NS_ERROR_NOT_INITIALIZED
No Zip file is open.
NS_ERROR_IN_PROGRESS
The queue is already in progress.
NS_ERROR_FILE_NOT_FOUND
No entry with the given path exists.

Other errors may occur if updating the Zip file fails.

Example

Adding a comment to a Zip file

var zipWriter = Components.Constructor("@mozilla.org/zipwriter;1", "nsIZipWriter");
var zipW = new zipWriter();

zipW.open(myZipFilePath, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
zipW.comment = "This is a comment.";
zipW.close();

PR_RDWR and friends are constants that are not in any interface (see bug 433295), so for the code above to work you need something like:

const PR_RDONLY      = 0x01;
const PR_WRONLY      = 0x02;
const PR_RDWR        = 0x04;
const PR_CREATE_FILE = 0x08;
const PR_APPEND      = 0x10;
const PR_TRUNCATE    = 0x20;
const PR_SYNC        = 0x40;
const PR_EXCL        = 0x80;

See PR_Open Documentation or File I/O Snippets for details.

Adding a file to a Zip archive

This code synchronously adds the file specified by the nsIFile theFile to the Zip archive.

var zipWriter = Components.Constructor("@mozilla.org/zipwriter;1", "nsIZipWriter");
var zipW = new zipWriter();

zipW.open(myZipFilePath, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
zipW.addEntryFile("Path/For/This/File/In/Zip Archive", Components.interfaces.nsIZipWriter.COMPRESSION_DEFAULT, theFile, false);
zipW.close();

The argument myZipFilePath isn't a path but rather it has to be an nsIFile instance specifying the location of the new zip file. The file need not exist, but the directory that contains it (nsIFile.parent) must exist.

Recursively Add All Contents of a Directory to Zip File

This example below can be copied and pasted into your scratchpad, set the environment to "Browser" and can run. What it does is shows a File Picker dialog and asks you to pick a folder. Once you pick a folder, it will create a zip file inside this folder with the same name as the folder. It will then recursively go through all contents in the folder selected and add them into this zip file. This example is also a good of example of the in AUTF8String aZipEntry argument of addEntryFile and how it is used, this entry is very important, it is basically what to save the file as in the zip, you can take a file "blah.exe" and make it save in the zip as "hi and no extension".

var {Cc: classes, Ci: interfaces, Cu: utils} = Components;
var zw = Cc['@mozilla.org/zipwriter;1'].createInstance(Ci.nsIZipWriter);
var pr = {PR_RDONLY: 0x01, PR_WRONLY: 0x02, PR_RDWR: 0x04, PR_CREATE_FILE: 0x08, PR_APPEND: 0x10, PR_TRUNCATE: 0x20, PR_SYNC: 0x40, PR_EXCL: 0x80}; //https://developer.mozilla.org/docs/PR_Open#Parameters
var fu = Cu.import('resource://gre/modules/FileUtils.jsm').FileUtils;

var fp = Cc['@mozilla.org/filepicker;1'].createInstance(Ci.nsIFilePicker);
fp.init(window, 'Select Directory to Compile', Ci.nsIFilePicker.modeGetFolder);
fp.appendFilters(Ci.nsIFilePicker.filterAll | Ci.nsIFilePicker.filterText);

var rv = fp.show();
if (rv == Ci.nsIFilePicker.returnOK) {
    var dir = fp.file;
    //dir must exist, as the user selected it. but note that if dir doesnt exist zw.open throws problems
    //var path = fp.file.path; //returns C:\Users\3K2KYC1\Documents\prefs\prefs

    var xpi = fu.File(dir.path + '\\' + dir.leafName + '.zip');

    zw.open(xpi, pr.PR_RDWR | pr.PR_CREATE_FILE | pr.PR_TRUNCATE); //PR_TRUNCATE overwrites if file exists //PR_CREATE_FILE creates file if it dne //PR_RDWR opens for reading and writing

    //recursviely add all
    var dirArr = [dir]; //adds dirs to this as it finds it
    for (var i=0; i<dirArr.length; i++) {
        Cu.reportError('adding contents of dir['+i+']: ' + dirArr[i].leafName + ' PATH: ' + dirArr[i].path);
        var dirEntries = dirArr[i].directoryEntries;
        while (dirEntries.hasMoreElements()) {
        	var entry = dirEntries.getNext().QueryInterface(Ci.nsIFile); //entry is instance of nsiFile so here https://developer.mozilla.org/docs/XPCOM_Interface_Reference/nsIFile
        	if (entry.path == xpi.path) {
        		Cu.reportError('skipping entry - will not add this entry to the zip file - as this is the zip itself: "' + xpi.path + '" leafName:"' + xpi.leafName + '"');
        		continue;
        	}
        	if (entry.isDirectory()) {
        	   dirArr.push(entry);
        	}
            var relPath = entry.path.replace(dirArr[0].path, ''); //need relative because we need to use this for telling addEntryFile where in the zip it should create it, and because zip is a copy of the directory
            Cu.reportError('+' + relPath); //makes it relative to directory the parent dir (dir[0]) so it can succesfully populate files with same names but different folders in this parent dir, needed because recursviely going through all dirs
            var saveInZipAs = relPath.substr(1); //need to get ride of the first '\' forward slash at start otherwise it puts every file added in a folder of its own.
            saveInZipAs = saveInZipAs.replace(/\\/g,'/'); //remember MUST use forward slash (/)
            Cu.reportError('--' + saveInZipAs);
            zw.addEntryFile(saveInZipAs, Ci.nsIZipWriter.COMPRESSION_NONE, entry, false);
        }
    }
    zw.close()
}

Other examples

For other examples, take a look at the unit tests in the source tree:

See also