Skip to content

Instantly share code, notes, and snippets.

@radzserg
Created October 10, 2016 12:46
Show Gist options
  • Select an option

  • Save radzserg/3abc7a6fdac6fb98af8607d1b5a0cdaf to your computer and use it in GitHub Desktop.

Select an option

Save radzserg/3abc7a6fdac6fb98af8607d1b5a0cdaf to your computer and use it in GitHub Desktop.
defmodule App.Image do
use App.Web, :model
alias App.Image
alias App.Repo
alias App.Helpers.FileHelper
alias App.Helpers.ImageHelper
schema "images" do
field :type, :string
field :path, :string
field :mime, :string
field :size, :integer
field :width, :integer
field :height, :integer
belongs_to :parent, App.Parent
timestamps()
end
@doc """
Builds a changeset based on the `struct` and `params`.
"""
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:type, :path, :mime, :size, :width, :height, :parent_id])
|> validate_required([:type, :path, :mime, :size])
end
@doc """
Creates image from provided URL
"""
def create_from_url(url, type) do
%HTTPoison.Response{body: body, headers: headers} = HTTPoison.get!(url)
mime = fetch_header_value(headers, "Content-Type")
size = fetch_header_value(headers, "Content-Length")
path = build_path_from_url(url, type)
save_image_from_content(body, path, mime, size, type)
end
@doc """
Return resized image from parent image
"""
def resized_image(image, width, height) do
case Repo.get_by(Image, parent_id: image.id, width: width, height: height) do
nil -> create_resized_image(image, width, height)
image -> image
end
end
@doc """
Create imge from local file
"""
def create_from_file(path, type, filename \\ nil) do
mime = FileHelper.get_mime!(path)
size = FileHelper.get_size!(path)
dest_path = if filename do
build_path_from_path(path, type, filename)
else
build_path_from_path(path, type)
end
save_image_from_file(path, dest_path, mime, size, type)
end
@doc """
Returns URL to the image
"""
def url(image) do
upload_manager = App.Pact.get("upload_manager")
upload_manager.url(image.path)
end
_ = """
Build path based on image type and base name
"""
defp build_path_from_url(image_url, type) do
uri = URI.parse(image_url)
basename = Path.basename(uri.path)
hash = random_string(32)
path = "#{type}/#{hash}/#{basename}"
path
end
defp build_path_from_path(path, type, filename) do
hash = random_string(32)
path = "#{type}/#{hash}/#{filename}"
path
end
defp build_path_from_path(path, type) do
basename = Path.basename(path)
hash = random_string(32)
path = "#{type}/#{hash}/#{basename}"
path
end
_ = """
Generates uniq string to build uniq path
"""
defp random_string(length) do
:crypto.strong_rand_bytes(length)
|> Base.url_encode64
|> binary_part(0, length)
end
_ = """
Fetch header from headers received with HTTPoison
"""
defp fetch_header_value(headers, needed_header) do
needed_header = String.downcase(needed_header)
header = Enum.find(headers, fn(header) ->
{header, _value} = header
String.downcase(header) == needed_header
end)
if header do
{header, value} = header
value
else
nil
end
end
_ = """
Creates resized image from given image
"""
defp create_resized_image(image, width, height) do
upload_manager = App.Pact.get("upload_manager")
base_path = Application.get_env(:tendsup, App.Endpoint)[:root]
local_path = "#{base_path}/runtime/resize_cache/" <> random_string(32)
upload_manager.save_to_local_file!(image.path, local_path)
ImageHelper.thumb(local_path, local_path, width, height)
path = build_path_from_path(image.path, image.type)
save_image_from_file(local_path, path, image.mime, image.size, image.type, image.id)
end
_ = """
Common private method used in create_from_url and create_from_path
"""
defp save_image_from_content(body, path, mime, size, type, parent_id \\ nil) do
changeset = Image.changeset(%Image{}, %{path: path, mime: mime,
size: size, type: type, parent_id: parent_id})
if changeset.valid? do
upload_manager = App.Pact.get("upload_manager")
case upload_manager.save_from_content!(body, path, %{content_type: mime}) do
:ok ->
case Repo.insert(changeset) do
{:ok, image} -> {:ok, image}
{:error, changeset} -> {:error, changeset}
end
{:error, reason} -> {:error, reason}
end
else
{:error, changeset}
end
end
defp save_image_from_file(file, path, mime, size, type, parent_id \\ nil) do
changeset = Image.changeset(%Image{}, %{path: path, mime: mime,
size: size, type: type, parent_id: parent_id})
if changeset.valid? do
upload_manager = App.Pact.get("upload_manager")
case upload_manager.save_from_file!(file, path, %{content_type: mime}) do
:ok ->
case Repo.insert(changeset) do
{:ok, image} -> {:ok, image}
{:error, changeset} -> {:error, changeset}
end
{:error, reason} -> {:error, reason}
end
else
{:error, changeset}
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment