Temporary files and directories, which are removed when the process that created them exits.
Fil.tmp/0 creates a directory and Fil.tmp/1 a file name in a directory of its own. Both return a plain Fil.Ref
on a local disk rooted at that directory, so the whole API works on it:
report = Fil.tmp("report.pdf")
{:ok, _} = Fil.cp(Fil.ref(invoices, "2026/09.pdf"), report)
{text, 0} = System.cmd("pdftotext", [Fil.Tmp.path(report), "-"])path/1 returns the absolute path of a ref on a temporary directory, for tools that need one. A tool can write into
the directory too, and Fil lists what it wrote:
frames = Fil.tmp()
{_, 0} = System.cmd("ffmpeg", ["-i", Fil.Tmp.path(video), Fil.Tmp.path(frames.disk, "%03d.png")])
{:ok, pngs} = Fil.ls(frames)Files in the directory are refs on its disk, such as Fil.ref(frames.disk, "001.png").
The directory
Each call creates a new directory in System.tmp_dir!/0 (which checks the TMPDIR, TMP and TEMP environment
variables), named fil- and a random suffix. Only the operating system user that runs the node can open it (mode
0o700), because a copy from a disk with an encryption plugin is plaintext here. On Windows the mode does nothing.
The name passed to Fil.tmp/1 is used as it is, so tools, mail attachments and Fil.Plugin.ContentType see the real
file name. It can have directories ("pages/1.png"), but it has to stay inside the directory: an absolute path, "",
"." or a name that escapes with .. raises ArgumentError. Pass a name from user input, such as upload.filename,
through Path.basename/1 first.
Cleanup
The directory belongs to the process that called Fil.tmp/0,1. It's removed with everything in it when that process
exits, also when it's killed, by a process of the :fil application. So:
- a temporary file created inside a
Taskis gone when the task returns. Create it in the caller and pass it in, or hand it to the caller withgive_away/2. - a long-lived process (a GenServer, a LiveView, a channel) keeps its temporary files for as long as it runs. So
does a request on Bandit over HTTP/1.1, whose process serves the whole keep-alive connection (see
Uploads and temporary files). Call
cleanup/1once it's done with them. A write to a temporary ref after that creates an ordinary directory, with the default mode, whichFildoesn't remove. Fil.rm_rf/1on the directory removes it, but it stays registered to its owner, and a later write creates it again without the0o700mode.cleanup/1removes both.- a temporary ref works only on the node that created it.
- stopping the
:filapplication removes every temporary directory. A crash of the whole node (kill -9, a power loss) leaves them behind in the system's temporary directory.
Fil.tmp/0,1 raises when the :fil application isn't running, because nothing would remove the directory then.
Summary
Functions
Removes the temporary directories of pid now. Returns :ok.
Makes pid the owner of a temporary directory, so it's removed when pid exits instead of the calling process. The
ref can name the directory or anything in it. Returns :ok.
Creates a temporary directory and returns a ref to it, with path ".". Fil.tmp/0 is the same function.
Creates a temporary directory and returns a ref to the file name in it. The file doesn't exist until something
writes it. Fil.tmp/1 is the same function.
Returns the absolute path of a ref on a temporary directory: of the directory itself, a file in it or anything else on its disk.
Returns the absolute path of path on a temporary directory's disk. See path/1.
Functions
@spec cleanup(pid()) :: :ok
Removes the temporary directories of pid now. Returns :ok.
For processes that run long after they're done with their temporary files. It never fails: a directory that can't
be removed completely is left as it is. It leaves the temporary files of Fil.Adapter.Local writes in progress
alone, so a write of pid still works. Cleaning up another process races with its give_away/2: a directory it hands
away meanwhile is removed, and path/1 then points to nothing.
Don't use the refs afterwards. A write to one creates an ordinary directory in its place, with the default mode,
which Fil doesn't remove.
Makes pid the owner of a temporary directory, so it's removed when pid exits instead of the calling process. The
ref can name the directory or anything in it. Returns :ok.
report = Fil.tmp("report.pdf")
Fil.write!(report, pdf)
:ok = Fil.Tmp.give_away(report, caller)If pid is dead already, the directory is removed right away. It raises ArgumentError when the calling process
doesn't own the directory, and for a pid on another node.
@spec new() :: Fil.Ref.t()
Creates a temporary directory and returns a ref to it, with path ".". Fil.tmp/0 is the same function.
iex> Fil.Tmp.new()
#Fil.Ref<local:.>When the directory can't be created, it raises the error struct Fil.Adapter.Local would return, such as a
Fil.AccessDeniedError or a Fil.StorageFullError, with the directory as :path. Without the :fil application
running, it raises a RuntimeError.
Creates a temporary directory and returns a ref to the file name in it. The file doesn't exist until something
writes it. Fil.tmp/1 is the same function.
iex> Fil.Tmp.new("pages/1.png")
#Fil.Ref<local:pages/1.png>The name has to be a relative path that stays inside the directory (see "The directory" above). Anything else raises
ArgumentError before a directory is created. Otherwise it raises like new/0.
Returns the absolute path of a ref on a temporary directory: of the directory itself, a file in it or anything else on its disk.
It raises ArgumentError for a ref on another disk, and after cleanup/1 or the owner's exit.
@spec path(Fil.Disk.t(), Path.t()) :: Path.t()
Returns the absolute path of path on a temporary directory's disk. See path/1.