{"id":13507932,"url":"https://github.com/keichan34/exfile","last_synced_at":"2025-04-09T18:18:17.881Z","repository":{"id":46686192,"uuid":"48341971","full_name":"keichan34/exfile","owner":"keichan34","description":"File upload persistence and processing for Phoenix / Plug","archived":false,"fork":false,"pushed_at":"2021-09-30T03:08:37.000Z","size":182,"stargazers_count":90,"open_issues_count":16,"forks_count":19,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-09T18:18:12.200Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/keichan34.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-12-21T00:03:37.000Z","updated_at":"2024-02-23T03:46:03.000Z","dependencies_parsed_at":"2022-08-27T19:10:14.438Z","dependency_job_id":null,"html_url":"https://github.com/keichan34/exfile","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keichan34%2Fexfile","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keichan34%2Fexfile/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keichan34%2Fexfile/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keichan34%2Fexfile/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/keichan34","download_url":"https://codeload.github.com/keichan34/exfile/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248085326,"owners_count":21045139,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T02:00:43.654Z","updated_at":"2025-04-09T18:18:17.863Z","avatar_url":"https://github.com/keichan34.png","language":"Elixir","funding_links":[],"categories":["Files and Directories"],"sub_categories":[],"readme":"# Exfile\n\n[![Build Status](https://travis-ci.org/keichan34/exfile.svg?branch=master)](https://travis-ci.org/keichan34/exfile) [![hex.pm](https://img.shields.io/hexpm/v/exfile.svg)](https://hex.pm/packages/exfile) [![hexdocs](https://img.shields.io/badge/hex-docs-brightgreen.svg)](http://hexdocs.pm/exfile/readme.html)\n[![Deps Status](https://beta.hexfaktor.org/badge/all/github/keichan34/exfile.svg)](https://beta.hexfaktor.org/github/keichan34/exfile)\n\nFile upload persistence and processing for Phoenix / Plug, with a focus on\nflexibility and extendability.\n\nInspired heavily by  [Refile](https://github.com/refile/refile). If you use\nRuby, check Refile out. I like it. 👍\n\nRequires Elixir `~\u003e 1.2`. At this point, it is tested against the most recent\nversions of Elixir (`1.2.6` and `1.3.1`). Feel free to check the Travis build\nout.\n\nExfile is used in a production environment at this point, but it still may go\nthrough some breaking changes. Exfile aims to adheres to\n[semver v2.0](http://semver.org/spec/v2.0.0.html).\n\n## Storage Adapters\n\nExfile supports storage backend adapters. A local filesystem based\nadapter is included (`Exfile.Backend.FileSystem`) as an example.\n\n* [exfile-b2](https://github.com/keichan34/exfile-b2) -- storage adapter for\n\tthe [Backblaze B2](https://www.backblaze.com/b2/cloud-storage.html) cloud\n\tstorage service.\n\n## File Processors\n\nExfile also supports file processors / filters. If you're working with\nimages, `exfile-imagemagick` is recommended.\n\n* [exfile-imagemagick](https://github.com/keichan34/exfile-imagemagick) -- uses\n\tImageMagick to resize, crop, and transform images.\n* [exfile-encryption](https://github.com/keichan34/exfile-encryption) -- encrypts\n\tfiles before uploading them and decrypts them after downloading them from the\n\tbackend.\n\n## Usage Overview\n\nExfile applies transforms on the fly; it only stores the original file in the\nstorage backend. It is expected to be behind a caching HTTP proxy and/or a\ncaching CDN for performance. Because dimensions and processors are determined\nby the path, it is authenticated with a HMAC to make sure it is not tampered\nwith.\n\nThe Phoenix integration comes with two helper functions, `exfile_url` and\n`exfile_path`.\n\nFor example, the following code will return a path to the `user`'s `profile_picture`\nthat is converted to JPEG (if not already in JPEG format) and limited to 1024 × 1024.\n\n```elixir\nexfile_url(@conn, @user.profile_picture, format: \"jpg\", processor: \"limit\", processor_args: [1024, 1024])\n```\n\nFor more information about what processors are available for images, check out\n[exfile-imagemagick](https://github.com/keichan34/exfile-imagemagick).\n\n## Installation\n\n1. Add exfile to your list of dependencies in `mix.exs`:\n\n\t```elixir\n\tdef deps do\n\t  [{:exfile, \"~\u003e 0.3.0\"}]\n\tend\n\t```\n\n2. Ensure exfile is started before your application:\n\n\t```elixir\n\tdef application do\n\t  [applications: [:exfile]]\n\tend\n\t```\n\n3. Mount the Exfile routes in your router.\n\n### Phoenix\n\n[There is a sample Phoenix application with Exfile integrated you can check out.](https://github.com/keichan34/phoenix_exfile_test_app)\n\n```elixir\ndefmodule MyApp.Router do\n  use MyApp.Web, :router\n\n  forward \"/attachments\", Exfile.Router\n  ...\n```\n\nTo use the `exfile_path` and `exfile_url` helpers, include the\n`Exfile.Phoenix.Helpers` module where you need it (probably in the `view`\nsection of your `web/web.ex` file).\n\nPhoenix uses `Plug.Parsers` with a 8 MB limit by default -- this affects Exfile\ntoo. To increase it, find `Plug.Parsers` in `MyApp.Endpoint` and add the `length`\noption:\n\n```elixir\ndefmodule MyApp.Endpoint do\n  use Phoenix.Endpoint, otp_app: :my_app\n\n  plug Plug.Parsers,\n    ...\n    length: 25_000_000 # bytes; any value you deem necessary\nend\n```\n\n### Plug\n\n```elixir\ndefmodule MyApp.Router do\n  use Plug.Router\n\n  forward \"/attachments\", to: Exfile.Router\n  ...\n```\n\n### Ecto Integration\n\nThe following example will upload a file to the backend configured as \"store\".\nIf you want to upload files to an alternate backend, please take a look at\n`Exfile.Ecto.File` and `Exfile.Ecto.FileTemplate` for instructions on making\na custom `Ecto.Type` for your needs.\n\n```elixir\ndefmodule MyApp.User do\n  use Ecto.Schema\n\n  schema \"users\" do\n    field :profile_picture, Exfile.Ecto.File\n  end\nend\n```\n\n```elixir\ndefmodule MyApp.Repo.Migrations.AddProfilePictureToUsers do\n  use Ecto.Migration\n\n  def change do\n    alter table(:users) do\n      add :profile_picture, :string\n    end\n  end\nend\n```\n\n### Validations\n\nExfile supports content type validation. Example of usage:\n\n```elixir\ndefmodule MyApp.User do\n  # definitions here\n\n  import Exfile.Ecto.ValidateContentType\n\n  def changeset(model, params) do\n    model\n    |\u003e cast(params, [:avatar])\n    |\u003e validate_content_type(:avatar, :image)\n    |\u003e validate_file_size(:avatar, 1_000_000) # Amount of bytes\n    |\u003e Exfile.Ecto.prepare_uploads([:avatar])\n  end\nend\n```\n\nYou can specify either an atom (could be `:image`, `:audio`, `:video`) or a list of strings\n`~w(image/bmp image/gif image/jpeg)`.\n\n### Storing metadata to the database\n\nYou can `cast_content_type` and store it to the database as a separate field. You need to\nhave a string field in your database and go:\n\n``` elixir\ndefmodule MyApp.User do\n  # definitions here\n\n  import Exfile.Ecto.CastContentType\n  import Exfile.Ecto.CastFilename\n\n  def changeset(model, params) do\n    model\n    |\u003e cast(params, [:avatar])\n    |\u003e cast_content_type(:avatar)\n    |\u003e cast_filename(:avatar)\n    |\u003e Exfile.Ecto.prepare_uploads([:avatar])\n  end\nend\n```\n\nBy default, exfile will save content type to the `avatar_content_type` field.\nThe filename will be saved to the `avatar_filename` field. You can specify\ncustom field as the third parameter of the `cast_content_type` or\n`cast_filename` function.\n\n## Configuration\n\nIn `config.exs`:\n\n```elixir\nconfig :exfile, Exfile,\n  secret: \"secret string to generate the token used to authenticate requests\",\n  cdn_host: \"root_url\", # nginx/other webserver endpoint for your website. Defaults to Phoenix HTTP endpoint\n  backends: %{\n    \"store\" =\u003e configuration for the default persistent store\n    \"cache\" =\u003e configuration for an ephemeral store holding temporarily uploaded content\n  }\n```\n\nSee `Exfile.Config` for defaults.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeichan34%2Fexfile","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkeichan34%2Fexfile","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeichan34%2Fexfile/lists"}