Skip to content
master
Switch branches/tags
Code

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
lib
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Exfile

Build Status hex.pm hexdocs Deps Status

File upload persistence and processing for Phoenix / Plug, with a focus on flexibility and extendability.

Inspired heavily by Refile. If you use Ruby, check Refile out. I like it. 👍

Requires Elixir ~> 1.2. At this point, it is tested against the most recent versions of Elixir (1.2.6 and 1.3.1). Feel free to check the Travis build out.

Exfile is used in a production environment at this point, but it still may go through some breaking changes. Exfile aims to adheres to semver v2.0.

Storage Adapters

Exfile supports storage backend adapters. A local filesystem based adapter is included (Exfile.Backend.FileSystem) as an example.

File Processors

Exfile also supports file processors / filters. If you're working with images, exfile-imagemagick is recommended.

  • exfile-imagemagick -- uses ImageMagick to resize, crop, and transform images.
  • exfile-encryption -- encrypts files before uploading them and decrypts them after downloading them from the backend.

Usage Overview

Exfile applies transforms on the fly; it only stores the original file in the storage backend. It is expected to be behind a caching HTTP proxy and/or a caching CDN for performance. Because dimensions and processors are determined by the path, it is authenticated with a HMAC to make sure it is not tampered with.

The Phoenix integration comes with two helper functions, exfile_url and exfile_path.

For example, the following code will return a path to the user's profile_picture that is converted to JPEG (if not already in JPEG format) and limited to 1024 × 1024.

exfile_url(@conn, @user.profile_picture, format: "jpg", processor: "limit", processor_args: [1024, 1024])

For more information about what processors are available for images, check out exfile-imagemagick.

Installation

  1. Add exfile to your list of dependencies in mix.exs:

    def deps do
      [{:exfile, "~> 0.3.0"}]
    end
  2. Ensure exfile is started before your application:

    def application do
      [applications: [:exfile]]
    end
  3. Mount the Exfile routes in your router.

Phoenix

There is a sample Phoenix application with Exfile integrated you can check out.

defmodule MyApp.Router do
  use MyApp.Web, :router

  forward "/attachments", Exfile.Router
  ...

To use the exfile_path and exfile_url helpers, include the Exfile.Phoenix.Helpers module where you need it (probably in the view section of your web/web.ex file).

Phoenix uses Plug.Parsers with a 8 MB limit by default -- this affects Exfile too. To increase it, find Plug.Parsers in MyApp.Endpoint and add the length option:

defmodule MyApp.Endpoint do
  use Phoenix.Endpoint, otp_app: :my_app

  plug Plug.Parsers,
    ...
    length: 25_000_000 # bytes; any value you deem necessary
end

Plug

defmodule MyApp.Router do
  use Plug.Router

  forward "/attachments", to: Exfile.Router
  ...

Ecto Integration

The following example will upload a file to the backend configured as "store". If you want to upload files to an alternate backend, please take a look at Exfile.Ecto.File and Exfile.Ecto.FileTemplate for instructions on making a custom Ecto.Type for your needs.

defmodule MyApp.User do
  use Ecto.Schema

  schema "users" do
    field :profile_picture, Exfile.Ecto.File
  end
end
defmodule MyApp.Repo.Migrations.AddProfilePictureToUsers do
  use Ecto.Migration

  def change do
    alter table(:users) do
      add :profile_picture, :string
    end
  end
end

Validations

Exfile supports content type validation. Example of usage:

defmodule MyApp.User do
  # definitions here

  import Exfile.Ecto.ValidateContentType

  def changeset(model, params) do
    model
    |> cast(params, [:avatar])
    |> validate_content_type(:avatar, :image)
    |> validate_file_size(:avatar, 1_000_000) # Amount of bytes
    |> Exfile.Ecto.prepare_uploads([:avatar])
  end
end

You can specify either an atom (could be :image, :audio, :video) or a list of strings ~w(image/bmp image/gif image/jpeg).

Storing metadata to the database

You can cast_content_type and store it to the database as a separate field. You need to have a string field in your database and go:

defmodule MyApp.User do
  # definitions here

  import Exfile.Ecto.CastContentType
  import Exfile.Ecto.CastFilename

  def changeset(model, params) do
    model
    |> cast(params, [:avatar])
    |> cast_content_type(:avatar)
    |> cast_filename(:avatar)
    |> Exfile.Ecto.prepare_uploads([:avatar])
  end
end

By default, exfile will save content type to the avatar_content_type field. The filename will be saved to the avatar_filename field. You can specify custom field as the third parameter of the cast_content_type or cast_filename function.

Configuration

In config.exs:

config :exfile, Exfile,
  secret: "secret string to generate the token used to authenticate requests",
  cdn_host: "root_url", # nginx/other webserver endpoint for your website. Defaults to Phoenix HTTP endpoint
  backends: %{
    "store" => configuration for the default persistent store
    "cache" => configuration for an ephemeral store holding temporarily uploaded content
  }

See Exfile.Config for defaults.

About

File upload persistence and processing for Phoenix / Plug

Resources

License

Packages

No packages published

Languages