Make a zip file in Elixir

Hello.
I have a text file and I want to make a zip file from it using Elixir or just to compress its folder.

files = File.ls!("path") 
           |> Enum.map(fn filename -> Path.join("path", filename) end)
           |> Enum.map(&String.to_charlist/1)

I then use :zip.create("foo.zip", files) to write the zip file. It returns a tuple: {:ok, "foo.zip"} but the file does not get saved.
Where is my mistake? Or can I get some sort of a solution? Thanks.

1 Like

The file is created in the working dir of the running BEAM VM, you can check with System.cwd/0.

3 Likes

And usually this is the folder that your ‘mix.exs’ is also in. If you are running in development that is.
When you have made a release, the folder location might be different.

2 Likes

Okay, zip file is created but there is another problem. It writes the whole path to the file, for example “/home/user/project/text.txt” and not just the file. Is there any other way just to compress the text file without its path?

According to the Erlang docs on :zip.create/3 there is an optional third argument with options where you could set the working directory for compression with [cwd: path]. The effect is that files will be looked up within that path and will omit the path in the compressed file.

Expanding on your example:

files =
  File.ls!(path)
  |> Enum.map(&String.to_charlist/1)

:zip.create("files.zip", files, cwd: path)
$ unzip -l files.zip
Archive:  files.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
   212151  09-22-2016 17:32   file1.txt
   333814  09-22-2016 17:32   file2.txt
   269066  09-22-2016 17:32   file3.txt
   333736  09-22-2016 17:32   file4.txt
   284213  09-22-2016 17:32   file5.txt
---------                     -------
  1432980                     5 files
8 Likes

Hello there, I’m new to elixir and when I wrote the same snippet I got the following warning:

The call zip:create(#{#<116>(8, 1, 'integer', ['unsigned', 'big']), #<101>(8, 1, 'integer', ['unsigned', 'big']), #<115>
(8, 1, 'integer', ['unsigned', 'big']), #<116>(8, 1, 'integer', ['unsigned', 'big']), #<46>(8, 1, 'integer', ['unsigned', 
'big']), #<97>(8, 1, 'integer', ['unsigned', 'big']), #<112>(8, 1, 'integer', ['unsigned', 'big']), #<107>(8, 1, 'integer',
 ['unsigned', 'big']), #<103>(8, 1, 'integer', ['unsigned', 'big'])}#,_files@1::[any()],[{'cwd',<<_:8,_:_*8>>},...]) will 
never return since the success typing is (atom() | [atom() | [any()] | char()],[atom() | [atom() | [any()] | char()] | 
{atom() | [atom() | [any()] | char()],binary()} | {atom() | [atom() | [any()] | char()],binary(),{'file_info','undefined' | 
non_neg_integer(),'device' | 'directory' | 'other' | 'regular' | 'symlink' | 'undefined','none' | 'read' | 'read_write' |
 'undefined' | 'write','undefined' | non_neg_integer() | {_,_},'undefined' | non_neg_integer() | {_,_},'undefined' |
 non_neg_integer() | {_,_},'undefined' | non_neg_integer(),'undefined' | non_neg_integer(),'undefined' | 
non_neg_integer(),'undefined' | non_neg_integer(),'undefined' | non_neg_integer(),'undefined' | 
non_neg_integer(),'undefined' | non_neg_integer()}}],['cooked' | 'memory' | 'verbose' | {'comment',string()} | 
{'compress','all' | [[any()]] | {'add',[any()]} | {'del',[any()]}} | {'cwd',string()} | {'uncompress','all' | [[any()]] | {'add',
[any()]} | {'del',[any()]}}]) -> {'error',_} | {'ok',atom() | [atom() | [any()] | char()] | {atom() | [atom() | [any()] | 
char()],binary()}} and the contract is (Name,FileList,Options) -> RetValue when Name :: file:name(), FileList :: 
[FileSpec], FileSpec :: file:name() | {file:name(),binary()} | {file:name(),binary(),file:file_info()}, Options :: 
[Option], Option :: create_option(), RetValue :: {'ok',FileName::filename()} | {'ok',
{FileName::filename(),binary()}} | {'error',Reason::term()}ElixirLS Dialyzer

any idea about why am I getting this?

Thanks in advance.

It seems that :zip.create expects Erlang string type which is actually charlist. So instead of "files.zip" you’d need to write something like 'files.zip'? Or is the conversion somehow performed automatically.

You are right, this function still does require a char list (atoms/nested list), and no there is no automatic conversion between elixir strings or char lists.