From bc7e0e5f8160cf1f2b2f65df423559e29dc4a06e Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Thu, 9 Oct 2025 16:07:30 +0200 Subject: [PATCH 1/2] Implement deleting an uploaded file If we want to clean memory within a very long-lived process, we currently have no way to do so. This attempts to offer a way to delete a file earlier when we know it won't be needed anymore but it's process owner will. --- lib/plug/upload.ex | 13 +++++++++++++ test/plug/upload_test.exs | 7 +++++++ 2 files changed, 20 insertions(+) diff --git a/lib/plug/upload.ex b/lib/plug/upload.ex index 53a4697a..585714df 100644 --- a/lib/plug/upload.ex +++ b/lib/plug/upload.ex @@ -59,6 +59,19 @@ defmodule Plug.Upload do end end + @doc """ + Deletes the given upload file. + """ + @spec delete(t | binary) :: :ok | {:error, term} + def delete(%__MODULE__{path: path}), do: delete(path) + + def delete(path) when is_binary(path) do + with :ok <- :file.delete(path, [:raw]) do + :ets.delete_object(@path_table, {self(), path}) + :ok + end + end + @doc """ Assign ownership of the given upload file to another process. diff --git a/test/plug/upload_test.exs b/test/plug/upload_test.exs index 89a218b9..432c7903 100644 --- a/test/plug/upload_test.exs +++ b/test/plug/upload_test.exs @@ -24,6 +24,13 @@ defmodule Plug.UploadTest do end end + test "removes the random file on request" do + {:ok, path} = Plug.Upload.random_file("sample") + File.open!(path) + :ok = Plug.Upload.delete(path) + wait_until(fn -> not File.exists?(path) end) + end + defp wait_until(fun) do if fun.() do :ok From 3296b6b773c194f4f7cdb5dfe6da142d6cac086f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 9 Oct 2025 16:27:58 +0200 Subject: [PATCH 2/2] Update lib/plug/upload.ex --- lib/plug/upload.ex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/plug/upload.ex b/lib/plug/upload.ex index 585714df..42e9b035 100644 --- a/lib/plug/upload.ex +++ b/lib/plug/upload.ex @@ -61,6 +61,9 @@ defmodule Plug.Upload do @doc """ Deletes the given upload file. + + Uploads are automatically removed when the current process terminates, + but you may invoke this to request the file to be removed sooner. """ @spec delete(t | binary) :: :ok | {:error, term} def delete(%__MODULE__{path: path}), do: delete(path)