agu-z / elm-zip / Zip

Work with Zip archives.


type Zip

Represents a Zip archive.

An archive is comprised of entries which represent files -that may be compressed- and directories.

Read an archive

fromBytes : Bytes -> Maybe Zip

Read a Zip from Bytes.

If you have an uploaded File of an archive, you can use File.toBytes to read it:

import File exposing (File)
import Task exposing (Task)
import Zip exposing (Zip)

type Msg
    = GotZip (Maybe Zip)

readArchive : File -> Cmd Msg
readArchive file =
    file
        |> File.toBytes
        |> Task.map Zip.fromBytes
        |> Task.perform GotZip

You can also get Bytes from somewhere else, such as an HTTP request, or even from within another archive.

Access the content

Once you have a Zip, you can use it to access its files and directories.

Use the Zip.Entry module to do read their content and metadata.

entries : Zip -> List Internal.Format.Entry

Get all entries in the archive.

allEntries =
    Zip.entries zip

Files and directories get their own entries.

If you only care about one kind, you can use the Zip.Entry.isDirectory function to filter them:

allFiles =
    zip
        |> Zip.entries
        |> List.filter (not << Entry.isDirectory)

getEntry : String -> Zip -> Maybe Internal.Format.Entry

Get an entry by its absolute path.

zip |> Zip.getEntry "versions/v1.txt"

Nothing is returned if no entry matches the path exactly.

Directory entries are typically stored in the archive with a slash at the end:

zip |> Zip.getEntry "versions" == Nothing

zip |> Zip.getEntry "versions/" == Just (Entry(..))

count : Zip -> Basics.Int

Count the number of entries in an archive.

isEmpty : Zip -> Basics.Bool

Determine if an archive is empty.

Build an archive

You can alter archives or create your own.

Checkout the Build section of the Zip.Entry module to learn how to make your own entries.

empty : Zip

An empty archive with no entries.

From here, you can use insert to add some entries.

fromEntries : List Internal.Format.Entry -> Zip

Create an archive from a list of entries.

insert : Internal.Format.Entry -> Zip -> Zip

Add a new entry to the archive.

This function replaces entries with the same path. You can conditionally add it by checking existence with the getEntry function:

case zip |> Zip.getEntry path of
    Nothing ->
        -- Entry does not exist, create and add it
        zip |> Zip.insert (createEntry ())

    Just _ ->
        -- Entry already exists, leave archive as it is
        zip

filter : (Internal.Format.Entry -> Basics.Bool) -> Zip -> Zip

Only keep entries that pass a given test.

Examples

Remove entries by path:

filter (\entry -> Entry.path entry /= "sample/version.json") zip

Keep all files under 1MB:

filter (\entry -> Entry.extractedSize entry < 1048576) zip

Keep only .txt files:

filter (Entry.path >> String.endsWith ".txt") zip

...and when it's ready

toBytes : Zip -> Bytes

Write a Zip to Bytes.

From here, you can download the archive, upload it to a server, etc.

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        DownloadArchive ->
            ( model
            , model.zip
                |> Zip.toBytes
                |> File.Download.bytes "archive.zip" "application/zip"
            )