Zip files and pass file_info

I have a list of file tuples for which I want to create a zip file in memory. The code works as expected, but now I have an extra requirement. The resulting files in the zip have the creation date set to ‘now’; which is to be expected. However I want to be able to set that creation date myself. (Long story short: I have the correct date for that data in the database).

But no matter how I pass the file_info to the zip method, I always end up getting {:error, :einval}

Here is my latest version, where I just used the file_info struct I could get from a file on disk:

defmodule ZipHelper do
  @doc """
  Creates a valid file_info tuple.

  `data` is the file binary.
  `date` is a tuple in the format {{year, month, day}, {hour, minute, second}}.
  """
  def make_file_info(data, date) do
    size = byte_size(data)
    type = :regular
    mode = 0o644
    # Using default values for the remaining fields:
    major_device = 0
    minor_device = 0
    inode = 0
    links = 1
    uid = 0
    gid = 0

    {:file_info, size, type, mode, date, date, date, mode, links, major_device, minor_device,
     inode, uid, gid}
  end
end

defmodule ZipHelperDemo do
  def run() do
    date = {{2022, 3, 14}, {10, 28, 2}}
    data = File.read!("dummy.pdf")
    {:ok, file_info} = :file.read_file_info("dummy.pdf")
    # file_info = ZipHelper.make_file_info(data, date)

    files = [
      {~c"dummy.pdf", data, [file_info]}
    ]

    path = "."
    :zip.create("full.zip", files, [{:cwd, path}])
  end
end

The third element of this tuple should be just file_info, not a list of file_info

1 Like

Oh my that was dumb… that gives me something more to work with. Still an error though…

{:error,
 {:EXIT,
  {:function_clause,
   [
     {:filename, :join,
      [
        ".",
        {~c"dummy.pdf",
              <<data...>>,
         {:file_info, 13264, :regular, 420, {{2022, 3, 14}, {10, 28, 2}},
          {{2022, 3, 14}, {10, 28, 2}}, {{2022, 3, 14}, {10, 28, 2}}, 420, 1, 0,
          0, 0, 0, 0}}
      ], []},
     {:zip, :put_z_files, 6, [file: ~c"zip.erl", line: 1133]},
     {:zip, :do_zip, 3, [file: ~c"zip.erl", line: 703]},
     {:zip, :zip, 3, [file: ~c"zip.erl", line: 692]},
     {:elixir, :eval_external_handler, 3, [file: ~c"src/elixir.erl", line: 386]},
     {:erl_eval, :do_apply, 7, [file: ~c"erl_eval.erl", line: 904]},
     {:elixir, :eval_forms, 4, [file: ~c"src/elixir.erl", line: 364]},
     {Module.ParallelChecker, :verify, 1,
      [file: ~c"lib/module/parallel_checker.ex", line: 120]},
     {IEx.Evaluator, :eval_and_inspect, 3,
      [file: ~c"lib/iex/evaluator.ex", line: 336]},
     {IEx.Evaluator, :eval_and_inspect_parsed, 3,
      [file: ~c"lib/iex/evaluator.ex", line: 310]},
     {IEx.Evaluator, :parse_eval_inspect, 4,
      [file: ~c"lib/iex/evaluator.ex", line: 299]},
     {IEx.Evaluator, :loop, 1, [file: ~c"lib/iex/evaluator.ex", line: 189]},
     {IEx.Evaluator, :init, 5, [file: ~c"lib/iex/evaluator.ex", line: 34]},
     {:proc_lib, :init_p_do_apply, 3, [file: ~c"proc_lib.erl", line: 329]}
   ]}}}

Update: apparently the {:cdw, path} option is conflicting here, when I remove that it works. Now I need to figure out why I have that option, I added it many months ago for good reason but I forgot ^^

I don’t think the three-tuple form is compatible with the cwd option. The error you’re getting is because add_cwd doesn’t have a clause for that shape:

There should likely be a clause:

add_cwd(_CWD, {_Name, _, _} = F) -> F;

to match the existing two-tuple version.

Note that the value passed in the cwd option isn’t used for anything if the data is supplied as a binary (the 2-tuple version). Unless you’re mixing {filename, contents} and plain filename in the input to :zip.create you shouldn’t need the option at all.

1 Like

Thank you @al2o3cr