stavro / arc

:paperclip: Flexible file upload and attachment library for Elixir
1.16k stars 210 forks source link

file is not getting written to server #264

Closed benonymus closed 5 years ago

benonymus commented 5 years ago

Hey my file upload is arriving to the server but not getting written to it's path, and the folders are not getting created on Ubuntu server, what do i need to do in order to make it work?

pedep commented 5 years ago

Are you using arc standalone, or arc_ecto If you are using arc with ecto, can you post an excerpt of you model Can you post your definition If you are using config.ex to configure arc, could you share your config

benonymus commented 5 years ago

I am using arc and arc_ecto. If I understand correctly, you are asking for these: If you meant some other files, please let me know!

defmodule Userteam1.Web.Recording do
  use Ecto.Schema
  use Arc.Ecto.Schema
  import Ecto.Changeset

  schema "recordings" do
    field(:path_to_recording, Userteam1Web.Recording.Type)
    field(:mod_score, :integer, default: 0)
    belongs_to(:user, Userteam1.Web.User)
    belongs_to(:challenge, Userteam1.Web.Challenge)
    has_many(:comment, Userteam1.Web.Comment)

    timestamps()
  end

  @doc false
  def changeset(recording, attrs) do
    recording
    |> cast(attrs, [:challenge_id, :user_id, :mod_score])
    |> cast_attachments(attrs, [:path_to_recording])
    |> validate_required([:path_to_recording])
  end
end
defmodule Userteam1Web.Recording do
  use Arc.Definition

  # Include ecto support (requires package arc_ecto installed):
  use Arc.Ecto.Definition

  @versions [:original]

  # To add a thumbnail version:
  # @versions [:original, :thumb]

  # Override the bucket on a per definition basis:
  # def bucket do
  #   :custom_bucket_name
  # end

  # Whitelist file extensions:
  # def validate({file, _}) do
  #   ~w(.jpg .jpeg .gif .png) |> Enum.member?(Path.extname(file.file_name))
  # end

  # Define a thumbnail transformation:
  # def transform(:thumb, _) do
  #   {:convert, "-strip -thumbnail 250x250^ -gravity center -extent 250x250 -format png", :png}
  # end

  # Override the persisted filenames:
  # def filename(version, _) do
  #   version
  # end

  # Override the storage directory:
  def storage_dir(_, {_, _}) do
    "uploads/recordings/"
  end

  # Provide a default URL if there hasn't been a file uploaded
  # def default_url(version, scope) do
  #   "/images/avatars/default_#{version}.png"
  # end
end
config :arc,
  storage: Arc.Storage.Local
pedep commented 5 years ago

Hmm.. I'm not able to reproduce the problem with your definition. The problem probably lies with the attrs you are creating the changeset with

Could you try posting the calling function, for when you are creating the Recording? Also, you should try IO.inspect'ing the attrs at the beginning of your changeset.

Forgot to ask earlier, but since you mention your server, does it work on your development machine? Are you getting any errors in the console?

benonymus commented 5 years ago

It does wokr on my dev machine, it creates the folders nicely - it is win 10 and th server is ubuntu

pedep commented 5 years ago

Ok.. thats a bit strange. I have no problems on my dev machine, which is running linux

How are you deploying your code to the server?

benonymus commented 5 years ago

mix release, an running it with start command, and serving it through nginx as reverse proxy

benonymus commented 5 years ago

git pull to the server

benonymus commented 5 years ago

actually if you feel like that it would help you can check out the code: https://github.com/benonymus/userteamroleapi

pedep commented 5 years ago

OK - So i have cloned and tested locally, and even when using releases i can't replicate your problem. But since your application does not have seeds for testing, i was unable to explore the user-interface, so i simply tested using iex and Userteam1Web.Recording.store("/path/to/local/file"). Since it works locally on your machine, i have to assume the problem lies with you usage of distillery

if you create a release by running mix release --env=prod on your production server, that should create a path something like this _build/prod/rel/userteam1/ If you launch your application like the output of mix release told you to (_build/prod/rel/userteam1/bin/userteam1 foreground), the uploaded resources will be placed here _build/prod/rel/userteam1/uploads/recordings/

Just to test this theory, you could try running find . -type d -name "recordings" and see if it spits out something similar to ./_build/prod/rel/userteam1/uploads/recordings

benonymus commented 5 years ago

ok i just run mix release, and then this find . -type d -name "recordings" and go this

./_build/dev/rel/userteam1/uploads/recordings

pedep commented 5 years ago

Alright - Then it seems like everything works as it should. You just have the uploads in the nested build folder

You should head over to https://hexdocs.pm/distillery/guides/phoenix_walkthrough.html and have a look through the Take that Release Anywhere part

benonymus commented 5 years ago

ok it is placed, but seems like the path to it is not saved correctly

benonymus commented 5 years ago

and that is the same on widows too, or rather the url for the picture to load it is not pointing to the picture

pedep commented 5 years ago

Yes i see - I am able to reproduce now. Though it seems the problem doesn't lie with arc, but rather your endpoint config. Maybe the endpoint conf in conjunction with the release

benonymus commented 5 years ago

i think this is the line

plug(Plug.Static, at: "/uploads", from: Path.expand("./uploads"), gzip: false)

hm, or not, but the files are getting saved to the correct location

benonymus commented 5 years ago

Actually i think it might be easier for you if you just got to localhost:4000 when you run it, and go to /users and create a user, then on creation you can see the picture after submission. Also then you can just edit that user's picture until it shows up, atleast that is the way I am trying to do

pedep commented 5 years ago

Alright - I think its solved. The problem seems to be

plug(Plug.Static, at: "/uploads", from: Path.expand("./uploads"), gzip: false)

resolving the path at compile-time. You should change this line to

plug(Plug.Static, at: "/uploads", from: "uploads", gzip: false)

Let me know if it works

benonymus commented 5 years ago

YES! seems to be working nicely, could you maybe give some explanation that why is it working like this? Also i just tried to run it with mix phx.server and with _build/prod/rel/userteam1/bin/userteam1 foreground this kinda command and both seems to be working, should the documentation changed?

pedep commented 5 years ago

It is because of the way (i assume) releases interact with the Path.expand gymnastics. This results in Plug.Static always pointing to the directory where mix release was run. So when Arc, which doesn't statically resolve the paths, puts its files relative to the release root, the plug still looks for the files in cwd .. or something like that

I got to this result by putting a IO.inspect Path.expand("uploads") in the endpoint.ex file. Here i noticed that it printed while running mix release, and not while starting the application. This led me to the explanation above. Changing the Path.expand to just a relative path lets Plug.Static do the magic for you https://github.com/elixir-plug/plug/blob/master/lib/plug/static.ex#L393

pedep commented 5 years ago

I actually noticed i have the same bug in my code, but since i use docker, i move my release to the build dir, resulting in the absolute path being the same as if it were relative

benonymus commented 5 years ago

I see, thanks a lot 🗡