keichan34 / exfile

File upload persistence and processing for Phoenix / Plug
MIT License
90 stars 19 forks source link

How to validate file format in exfile? #26

Closed asiniy closed 8 years ago

asiniy commented 8 years ago

Like this functionality in refile. I haven't found anything in this repo by keywords like valid, content_type, format and so on...

keichan34 commented 8 years ago

It's possible, but it's not very easy at the moment. exfile_imagemagick has a "metadata" processor. You can use this processor to extract the metadata, then add the validation error yourself via Ecto.Changeset.add_error/4. Here's an example:

defp validate_image_content_type(changeset) do
  meta = with {:ok, image} <- Ecto.Changeset.get_field(changeset, :image),
              {:ok, file} <- Exfile.File.open(image),
              {:ok, %{meta: meta}} <- ExfileImagemagick.Metadata.call(file, [], []),
              do: {:ok, meta}

  case meta do
    {:ok, %{"format" => format}} when format in ~w(JPEG PNG GIF) ->
      changeset
    _ ->
      Ecto.Changeset.add_error(changeset, :image, "invalid format")
  end
end

(I haven't tested this directly, but I use something like that in one of my apps. You can simplify it a bit more by adding the metadata processor as a pre-processor in the configuration if you're going to be verifying all uploads to that backend)

keichan34 commented 8 years ago

I'll be working on making file format validation easier in the near future (#27). Thanks for bringing this up!

asiniy commented 8 years ago

@keichan34 I want to work on this. May I do that? I can start even tomorrow

asiniy commented 8 years ago

btw, I think we can close issue

asiniy commented 8 years ago

@keichan34 your code raises exception for not-image files. Identify raises exception. The working solution:

  defp validate_video_format(changeset) do
    meta = with { :ok, video } <- Ecto.Changeset.fetch_change(changeset, :video),
                { :ok, file } <- Exfile.File.open(video),
                { result, 0 } <- System.cmd("file", ["--mime-type", file.path]),
                [_, mime_type] <- result |> String.strip |> String.split(": "),
                do: { :ok, mime_type }

    case meta do
      {:ok, mime_type} when mime_type in ~w(video/quicktime video/mp4 video/x-msvideo video/x-ms-wmv video/x-flv video/3gpp) ->
        changeset
      _ ->
        Ecto.Changeset.add_error(changeset, :video, "invalid format")
    end
  end

Will use it while work on 0.4.0 version