Want to check file size of image uploaded by user for validation

I have created an image upload feature in my phoenix project but want to apply a check for the file size of the image uploaded by the user. I have used the code as shown below to get the size but it is giving me an error. I think I am doing a wrong pattern match. What is the correct way to get the size of an image uploaded by user?

%{“size”=> size} = File.stat photo.path

Below is the error I am getting:

** (exit) an exception was raised:
** (MatchError) no match of right hand side value: {:ok, %File.Stat{access: :read_write, atime: {{2019, 10, 31}, {5, 20, 11}}, ctime: {{2019, 10, 31}, {5, 20, 11}}, gid: 20, inode: 10802062, links: 1, major_device: 16777217, minor_device: 0, mode: 33188, mtime: {{2019, 10, 31}, {5, 20, 11}}, size: 1816, type: :regular, uid: 502}}

With File.stat (the function without an exclamation mark), you need to include the :ok of the right side result on the left of your match, probably in case.

3 Likes

After getting image file size when I added the size condition in if statement for image should not greater than 2000 bytes I am getting error

“function true.filename/0 is undefined (module true is not available)”

The code that I have used is shown below:

def update_profile_pic(conn, %{"id" => id, "photo" => photo} = user_params, current_user) do

    IO.inspect id
    IO.inspect photo

    %{size: size} = File.stat! photo.path

    IO.inspect "size=#{size}"


    if upload = user_params["photo"] && size <= System.get_env("PROFILE_IMAGE_SIZE") do

	    .....
	    ....  code to upload new image 
	    ....

	    conn |> redirect(to: Routes.dashboard_path(conn, :dashboard))
	    else
	    conn |> put_flash(:error, "Could not upload image") |> redirect(to: Routes.dashboard_path(conn, :dashboard))  
    end
end

NOTE: if I remove the extra condition of size it starts working but I want to apply size check as it is necessary for me. Please suggest

This will evaluate user_params["photo"] && size <= System.get_env("PROFILE_IMAGE_SIZE") and bind the result to upload, you probably want to use ==/2, though there is no upload before that…

2 Likes

Maybe this will interrest you if you want to handle file upload without third-party libraries.

Step-by-Step Tutorial to Build a Phoenix App that Supports User Upload

1 Like

I’m really interested in what others do here when it comes to picture uploads. One of the attack vectors I’ve encountered in the past is embedded javascript and php code inside picture uploads that get executed when someone looks at the picture on the web page. How do you strip the exif header that might contain this malicious code in a picture when you upload it?

I think this a good solution but I guess we do not want or necessarily have the means to pay for this kind of service for a small non-profit project for example.

You can try Waffle but for the kind of attack you mention, you might have to include your own validation within your uploader module config.

defmodule Avatar do
  use Waffle.Definition

  def validate({file, _}) do
     your_custom_validation?
  end
end

Not to brag, but I think it will be cheaper to use some file store than to pay for all the hardware, especially as for most of the time you will not be using the hard drive. So I would say that S3 is actually cheaper if you are small non-profit project rather than storing it locally. And even if you really want to store it on the same server then I would say that booting up MinIO would be simpler and more future-proof in case after some time you decide that S3 in fact is cheaper. I have been there, I highly recommend that path rather than fighting with the migration later.

That doesn’t work, because it still passes as a valid file… I might have to write one.

In fact when I deploy my application on a VPS like Digital Ocean ones, I have already some storage that can be used to store some uploaded files as long as they are not that much. So I will pay more if I pay for a S3 bucket.

Thanks, I will take a look at that.

Yes that is what I called your_custom_validation?.
That should be a function that return true or false.
The default validation of the library will just check for allowed files extensions. So that is how to override it.

An easy way to see if it is valid is to let imagemagick try to eat it. ^.^

better don’t, you might be needlessly exposing yourself to a RCE/DoS targetting one of imagemagick’s vulnerabilities.

So do it in a docker container with a timed kill is what I hear you saying? ^.^

1 Like

haha, now you have a whole cluster of problems :wink:

3 Likes

I really like that! :smiley: