Skip to content

Repository files navigation

Bow

File uploads for Elixir

Features

  • Generate multiple dependent versions of a file
  • Integration with Ecto
  • Allow downloading remote files (remote_avatar_url params etc.)
  • Multiple storage adapters (local disk, Amazon S3)

Installation

defdepsdo[{:bow,"~> 0.4.3"},# for AWS S3 support{:ex_aws,"~> 2.0"},{:ex_aws_s3,"~> 2.0"},# for Bow.Exec{:erlexec,"~> 1.7.0"}]end

Usage

Minimal uploader definition

defmoduleMyUploaderdouseBow.Uploader# specify storage directorydefstore_dir(_file)do"uploads"endend

Full uploader example

defmoduleAttachmentUploaderdouseBow.Uploader# define what versions to generate for given filedefversions(_file)do[:original,:thumb]end# keep the origianal file namedeffilename(file,:original),do: file.name# prepend "thumb_" for thumbnaildeffilename(file,:thumb),do: "thumb_\#{file.name}"# do nothing with original filedeftransform(file,:original),do: transform_original(file)# generate image thumbnaildeftransform(source,target,:thumb)doBow.Exec.execsource,target,"convert ${input} -strip -gravity Center -resize 250x175^ -extent 250x175 ${output}"end# specify storage directorydefstore_dir(file)do"attachments/\#{file.scope.id}"end# specify storage optionsdefstore_options(_file)do[acl: :public_read]endend

Usage with Ecto

# Add `use Bow.Ecto` to the uploaderdefmoduleMyApp.UserAvatarUploaderdouseBow.UploaderuseBow.Ecto# <---- HERE# file.scope will be the user structdefstore_dir(file)do"users/#{file.scope.id}/avatar"endend# add avatar field to users tabledefmoduleMyApp.Repo.Migrations.AddAvatarToUsersdouseEcto.Migrationdefchangedoaltertable(:users)doadd:avatar,:stringendendend# use `MyApp.UserAvatarUploader.Type` as field typedefmoduleMyApp.Userdoschema"users"dofield:email,:stringfield:avatar,MyApp.UserAvatarUploader.Type# <-- HEREenddefchangeset(model\\%__MODULE__{},params)domodel|>cast(params,[:email,:avatar])# uncomment to add support for remote_avatar_url params# |> Bow.Ecto.cast_uploads(params, [:avatar])|>Bow.Ecto.validate()# optional validation using uploader rulesendend# create user and save fileschangeset=User.changeset(%User{},params)with{:ok,user}<-Repo.insert(changeset),{:ok,user,_}<-Bow.Ecto.store(user)do{:ok,user}end

Getting file URL

With standalone uploaders:

file=MyUploader.new("path/to/file.png")Bow.url(file)# => url of original fileBow.url(file,:thumb)# => url of thumb version

With Ecto integration:

user=Repo.get(User,1)Bow.Ecto.url(user,:avatar)# url of avatar originalBow.Ecto.url(user,:avatar,:thumb)# url of avatar thumbBow.Ecto.url(user,:photo,:thumb,signed: true)# you can pass storage-specific options

Overwriting file name

You can change the file name using uploader's cast/1 callback:

defmoduleTimestampUploaderdouseBow.UploaderuseBow.Ectodefcast(file)do# replace "myfile.png" with "avatar_12343456.png"ts=DateTime.utc_now|>DateTime.to_unixBow.set(file,:rootname,"avatar_#{ts}")enddefstore_dir(_file),do: "timestamp"end

Validation

You can overwrite validate/1 to add validations for e.g. allowed extension.

defmoduleAvatarUploaderdo# ...defvalidate(%{ext: ext})whenextin~w(.jpg .png),do: :okdefvalidate(_),do: {:error,:extension_not_allowed}# ...end

Using Bow in test environment

It is best to use local storage adapter when testing.

# config/test.exsconfig:bow,storage: Bow.Storage.Local,storage_prefix: "tmp/bow/"

Running Bow tests

mix test

Testing ecto integration

# edit config/config.exs# create test database
MIX_ENV=test mix ecto.create
# run tests
mix test --only ecto

Testing S3 adapter

# install fake-s3 gem
gem install fakes3
# start fake-s3 server
fakes3 -r tmp/s3 -p 4567
# run tests
mix test --only s3

About

File uploads for Elixir (with Ecto support)

Topics

Resources

Stars

28 stars

Watchers

23 watching

Forks

Releases

Packages

Used by

Contributors

Languages