Creating an Epub file with a Zip library
Asked Answered
G

3

11

HI All,

I am trying to zip up an Epub file i have made using c#

Things I have tried

  • Dot Net Zip http://dotnetzip.codeplex.com/
  • - DotNetZip works but epubcheck fails the resulting file (**see edit below)
  • ZipStorer zipstorer.codeplex.com
  • - creates an epub file that passes validation but the file won't open in Adobe Digital Editions
  • 7 zip
  • - I have not tried this using c# but when i zip the file using there interface it tells me that the mimetype file name has a length of 9 and it should be 8

In all cases the mimetype file is the first file added to the archive and is not compressed

The Epub validator that I'am using is epubcheck http://code.google.com/p/epubcheck/

if anyone has succesfully zipped an epub file with one of these libraries please let me know how or if anyone has zipped an epub file successfully with any other open source zipping api that would also work.


EDIT

DotNetZip works, see accepted answer below.

Grosvenor answered 5/5, 2011 at 13:49 Comment(3)
are you saying that those utilities fail to zip your file, or that when using gzip (or some other function) c# returns an error or that your epub file has issues?Dragonhead
the epub file is made without error but when i run the validator against it i get an error about the mimetype fileGrosvenor
And what if you write your files and the use WinZip ? Does it validate?Varnado
P
14

If you need to control the order of the entries in the ZIP file, you can use DotNetZip and the ZipOutputStream.

You said you tried DotNetZip and it (the epub validator) gave you an error complaining about the mime type thing. This is probably because you used the ZipFile type within DotNetZip. If you use ZipOutputStream, you can control the ordering of the zip entries, which is apparently important for epub (I don't know the format, just surmising).


EDIT

I just checked, and the epub page on Wikipedia describes how you need to format the .epub file. It says that the mimetype file must contain specific text, must be uncompressed and unencrypted, and must appear as the first file in the ZIP archive.

Using ZipOutputStream, you would do this by setting CompressionLevel = None on that particular ZipEntry - that value is not the default.

Here's some sample code:

private void Zipup()
{
    string _outputFileName = "Fargle.epub";
    using (FileStream fs = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite ))
    {
        using (var output= new ZipOutputStream(fs))
        {
            var e = output.PutNextEntry("mimetype");
            e.CompressionLevel = CompressionLevel.None;

            byte[] buffer= System.Text.Encoding.ASCII.GetBytes("application/epub+zip");
            output.Write(buffer,0,buffer.Length);

            output.PutNextEntry("META-INF/container.xml");
            WriteExistingFile(output, "META-INF/container.xml");
            output.PutNextEntry("OPS/");  // another directory
            output.PutNextEntry("OPS/whatever.xhtml");
            WriteExistingFile(output, "OPS/whatever.xhtml");
            // ...
        }
    }
}

private void WriteExistingFile(Stream output, string filename)
{
    using (FileStream fs = File.Open(fileName, FileMode.Read))
    {
        int n = -1;
        byte[] buffer = new byte[2048];
        while ((n = fs.Read(buffer,0,buffer.Length)) > 0)
        {
            output.Write(buffer,0,n);
        }
    }
}

See the documentation for ZipOutputStream here.

Postcard answered 5/5, 2011 at 13:57 Comment(2)
thank you so much for your quick well explained response just 2 quick things i had to change: output.PutNextEntry("META-INF/"); is not needed and just makes an empty META-INF file in the zip folder, and down in the WriteExistingFile I changed _outputFileName to filename just figured it may help anyone else looking at your answer thanks againGrosvenor
Hey claybo, thanks. I modified the sample code to fix those two things. I wasn't sure about the need for OPS/ (content directory) and META-INF/ (directory for meta content) within the epub file.Postcard
T
7

Why not make life easier?

private void IonicZip()
{
    string sourcePath = "C:\\pulications\\";
    string fileName = "filename.epub";

    // Creating ZIP file and writing mimetype
    using (ZipOutputStream zs = new ZipOutputStream(sourcePath + fileName))
    {
        var o = zs.PutNextEntry("mimetype");
        o.CompressionLevel = CompressionLevel.None;

        byte[] mimetype = System.Text.Encoding.ASCII.GetBytes("application/epub+zip");
        zs.Write(mimetype, 0, mimetype.Length);
    }

    // Adding META-INF and OEPBS folders including files     
    using (ZipFile zip = new ZipFile(sourcePath + fileName))
    {
        zip.AddDirectory(sourcePath  + "META-INF", "META-INF");
        zip.AddDirectory(sourcePath  + "OEBPS", "OEBPS");
        zip.Save();
    }
}
Torrens answered 6/9, 2012 at 11:49 Comment(1)
Thanks. This helped me write an ePub builder as 7zip was failing me. 7zip didn't seem to be able to work the mimetype right. I also have it doing multiple builds (B&N, Kindle, iBooks, Short Sample) by passing in a list of the files to compress for each eBook type.Howrah
P
1

For anyone like me who's searching for other ways to do this, I would like to add that the ZipStorer class from Jaime Olivares is a great alternative. You can copy the code right into your project, and it's very easy to choose between 'deflate' and 'store'.

https://github.com/jaime-olivares/zipstorer

Here's my code for creating an EPUB:

Dictionary<string, string> FilesToZip = new Dictionary<string, string>()
{
    { ConfigPath + @"mimetype",                 @"mimetype"},
    { ConfigPath + @"container.xml",            @"META-INF/container.xml" },
    { OutputFolder + Name.Output_OPF_Name,      @"OEBPS/" + Name.Output_OPF_Name},
    { OutputFolder + Name.Output_XHTML_Name,    @"OEBPS/" + Name.Output_XHTML_Name},
    { ConfigPath + @"style.css",                @"OEBPS/style.css"},
    { OutputFolder + Name.Output_NCX_Name,      @"OEBPS/" + Name.Output_NCX_Name}
};

using (ZipStorer EPUB = ZipStorer.Create(OutputFolder + "book.epub", ""))
{
    bool First = true;
    foreach (KeyValuePair<string, string> File in FilesToZip)
    {
        if (First) { EPUB.AddFile(ZipStorer.Compression.Store, File.Key, File.Value, ""); First = false; }
        else EPUB.AddFile(ZipStorer.Compression.Deflate, File.Key, File.Value, "");
    }
}

This code creates a perfectly valid EPUB file. However, if you don't need to worry about validation, it seems most eReaders will accept an EPUB with a 'deflate' mimetype. So my previous code using .NET's ZipArchive produced EPUBs that worked in Adobe Digital Editions and a PocketBook.

Prole answered 9/6, 2019 at 17:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.