Skip to content

Instantly share code, notes, and snippets.

@ryochin
Created December 20, 2023 04:31
Show Gist options
  • Save ryochin/681f4aada60edd1a6af568ee2ca06fb6 to your computer and use it in GitHub Desktop.
Save ryochin/681f4aada60edd1a6af568ee2ca06fb6 to your computer and use it in GitHub Desktop.
Elixir: Create a ZIP archive from list of files
defmodule Zip do
@moduledoc """
Easy ZIP creator
you need to install zstream:
```
{:zstream, "~> 0.6"}
```
"""
require Logger
@type size :: non_neg_integer
@opts_default compress: true,
zip64: true
@doc """
Create a ZIP archive from list of files
### Option
* zip64: boolean (default: true)
* compress: boolean (default: true)
### Example
```
iex> Zip.create_zip("archive.zip", "./test", ["assets/text.txt"])
{:ok, 187530}
```
"""
@spec create_zip(Path.t(), Path.t(), list(Path.t()), list) :: {:ok, size} | {:error, any}
def create_zip(zip_file, dir, list, opts \\ []) when is_list(opts) do
Logger.info("creating zip archive... (#{zip_file})")
opts = Keyword.merge(@opts_default, opts)
try do
:ok =
list
|> Enum.map(&zip_entry(dir, &1, opts))
|> Zstream.zip(zip64: opts[:zip64])
|> Stream.into(File.stream!(zip_file))
|> Stream.run()
case File.stat(zip_file) do
{:ok, %{size: size}} ->
Logger.debug("zip archive '#{zip_file}' created. (#{size} bytes)")
{:ok, size}
{:error, reason} ->
{:error, reason}
end
rescue
e in File.Error ->
%File.Error{reason: reason, path: path} = e
{:error, "#{:file.format_error(reason)} (#{path})"}
end
end
@spec zip_entry(Path.t(), Path.t(), list) :: Zstream.entry()
defp zip_entry(dir, file, opts) do
coder = if opts[:compress], do: Zstream.Coder.Deflate, else: Zstream.Coder.Stored
Zstream.entry(file, File.stream!(Path.join(dir, file), [], 8192), coder: coder)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment