WFransen

WFransen

Problem serving multiple dynamic images with Phoenix

I am trying to serve images that the user uploaded. then in an “overview page”, i want to put thumbnail-ish images in the html, which load sometimes and sometimes not.

Quick demo with a gif → gif doesn’t work apparently..:
ftr-image-bug
gif just emphasizes that when you refreshes, sometimes image 1 doesn’t work and the other one works, sometimes they work both and sometimes none.

When I go to the image link (/cases/image/1 for example), it loads every time. It is only on the page in the gif that it fails. When I get the failed image icon, the following error occurs:

[info] GET /case/image/2
[error] #PID<0.6379.0> running FirsttimerightDashboardWeb.Endpoint (connection #PID<0.6378.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: GET /case/image/1
** (exit) an exception was raised:
    ** (File.Error) could not read file stats "uploads/images/1-user_upload_3.png": no such file or directory

Yet the file is there. The information in the database is also correct (as far as i know of course…).
DB output:


The images are also in the folder with the correct name.

Also tried with ctrl + r/f5 force purge cache reload, though it doesn’t seem to help.

Thank you in advance.

Marked As Solved

WFransen

WFransen

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).

Also Liked

WFransen

WFransen

Hi Jetmush,

I really didn’t expect someone else looking at this code after a year ^^’ After looking at it again, the version you saw only supported one file at a time. I quickly adjusted this (it really isn’t much work! Just add the ‘multiple: true’ option in your template and you’ll receive an array of plug.Upload structs.)

After uploading 2 files, this is what you should see in a IEx.pry’d controller action:

pry(1)> up
[       
  %Plug.Upload{
    content_type: "application/octet-stream",
    filename: "bcm2708-rpi-b.dtb",
    path: "/tmp/plug-1595/multipart-1595601713-270993472630691-4"
  },
  %Plug.Upload{
    content_type: "application/octet-stream",
    filename: "bcm2708-rpi-b-plus.dtb", 
    path: "/tmp/plug-1595/multipart-1595601713-367099312190116-4"
  }
]

I left the pry in the repo so that you can clone, compile, run and see what happens. Now specifically for your question regarding:

multiple images in one database field.

I prefer storing the images on a AWS S3, or on the file system and store a path in the database. Since an upload is mostly one file, I’d implement uploading multiple files as multiple single uploads (and thus multiple database entries). Is it important that you keep track of the fact which files were uploaded in a single batch? If so, I’d personally suggest either one of:

  • Add a new table, e.g. “batch upload”. Then you can add a FK in your uploads as to which “batch upload” they belong.
  • I’d personally rather not suggest this, though I cannot put my finger on to the “why” part, but you could separate your file names with a unique separator. After then reading the entry, split on that separator and you have a list of filenames. You might resolve this with saving it as a JSON entry I think, but then what when you e.g. periodically check the integrity of files and one file is corrupt / no longer present?

With the second approach it seems to me a lot can go wrong and should be solved with code (e.g. the removal of a file means a bunch of code, while in essence it could be just one small table entry deletion). Hence my preference for the first solution, though I’d recommend this with URL’s to AWS S3 (definitely more scalable :wink: )

Note: whole rant above is if you’d want to keep track of batch uploads. If this is not necessary, I’d suggest not to add unnecessary complexity and see each file as a separate upload in its respective table.

In case other people read this and find this absolute blasphemy, please do tell me! Always eager to learn!!!

Hope this helped!

Last Post!

WFransen

WFransen

Hi Jetmush,

I really didn’t expect someone else looking at this code after a year ^^’ After looking at it again, the version you saw only supported one file at a time. I quickly adjusted this (it really isn’t much work! Just add the ‘multiple: true’ option in your template and you’ll receive an array of plug.Upload structs.)

After uploading 2 files, this is what you should see in a IEx.pry’d controller action:

pry(1)> up
[       
  %Plug.Upload{
    content_type: "application/octet-stream",
    filename: "bcm2708-rpi-b.dtb",
    path: "/tmp/plug-1595/multipart-1595601713-270993472630691-4"
  },
  %Plug.Upload{
    content_type: "application/octet-stream",
    filename: "bcm2708-rpi-b-plus.dtb", 
    path: "/tmp/plug-1595/multipart-1595601713-367099312190116-4"
  }
]

I left the pry in the repo so that you can clone, compile, run and see what happens. Now specifically for your question regarding:

multiple images in one database field.

I prefer storing the images on a AWS S3, or on the file system and store a path in the database. Since an upload is mostly one file, I’d implement uploading multiple files as multiple single uploads (and thus multiple database entries). Is it important that you keep track of the fact which files were uploaded in a single batch? If so, I’d personally suggest either one of:

  • Add a new table, e.g. “batch upload”. Then you can add a FK in your uploads as to which “batch upload” they belong.
  • I’d personally rather not suggest this, though I cannot put my finger on to the “why” part, but you could separate your file names with a unique separator. After then reading the entry, split on that separator and you have a list of filenames. You might resolve this with saving it as a JSON entry I think, but then what when you e.g. periodically check the integrity of files and one file is corrupt / no longer present?

With the second approach it seems to me a lot can go wrong and should be solved with code (e.g. the removal of a file means a bunch of code, while in essence it could be just one small table entry deletion). Hence my preference for the first solution, though I’d recommend this with URL’s to AWS S3 (definitely more scalable :wink: )

Note: whole rant above is if you’d want to keep track of batch uploads. If this is not necessary, I’d suggest not to add unnecessary complexity and see each file as a separate upload in its respective table.

In case other people read this and find this absolute blasphemy, please do tell me! Always eager to learn!!!

Hope this helped!

Where Next?

Popular in Questions Top

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

Other popular topics Top

KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36689 110
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40082 209
New

We're in Beta

About us Mission Statement