Search code examples
elixirphoenix-frameworkdynamic-image-generation

Problem serving multiple dynamic images with Phoenix (some load, some dont)


Technology: Phoenix 1.4.9 Problem : serving uploaded images in a webpage with the "img" tag, results in some being able to load and others are not.

Tried checking the image link, but the image link (a.e. /image/1 or /image/2) loads every time without problems.

Force cache purge also doesn't prove any indication to the problem.

If people want to play with the code themselves, here is the git repo: https://github.com/WannesFransen1994/phoenix-dynamic-images

what i think is important bits of code: Controller:

def index(conn, _params) do
    images = Repo.all(Image)
    render(conn, "index.html", images: images)
  end

  def create(conn, %{"upload" => %Plug.Upload{} = up}) do
    {:ok, _u} = up |> ImageContext.create_image()
    redirect(conn, to: Routes.page_path(conn, :index))
  end

  def display(conn, %{"id" => id}) do
    i = Repo.get(Image, id)
    conn |> put_resp_content_type(i.content_type) |> send_file(200, Image.local_path(i))
  end

ImageContext.ex

def create_image(%{filename: _, path: tmp_path, content_type: _} = upload) do
    hash = File.stream!(tmp_path, [], 2048) |> Image.sha256()

    with {:ok, %File.Stat{size: size}} <- File.stat(tmp_path),
         data_merged <- Map.from_struct(upload) |> Map.merge(%{size: size, hash: hash}),
         {:ok, upload_cs} <- %Image{} |> Image.changeset(data_merged) |> Repo.insert(),
         :ok <- tmp_path |> File.cp(Image.local_path(upload_cs)) do
      {:ok, upload_cs}
    else
      {:error, reason} -> Repo.rollback(reason)
    end
  end

Image schema (without the changeset etc...)

schema "images" do
    field :filename, :string
    field :content_type, :string
    field :hash, :string
    field :size, :integer
  end
def local_path(%Image{} = upload) do
    [@upload_directory, "#{upload.id}-#{upload.filename}"] |> Path.join()
  end

The output (on the image display link) works, but when I go to the overview page where all the images with the "img" tags get generated, they randomly fail to load (sometimes they all work, sometimes none, sometimes half, sometimes one works and the others don't)

Strange thing is that when you check the logs you get the following error:

** (exit) an exception was raised:
    ** (File.Error) could not read file stats "uploads/images/1-user_upload_3.png": no such file or directory

while the file is there and it works when you reload, or view the image apart.

EXTRA: image of the problem. Same page, just reloaded twice: samplescreenshot1samplescreenshot2


Solution

  • Solution was to work with absolute paths.

    Apparently Phoenix sometimes changes the current working directory, for example when code reloading, and thus the relative path fails.

    Credit goes to Nobbz (Slack) and Jose Valim (Git issue).