Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActiveStorage::FileNotFoundError in before_save #36994

Closed
franktisellano opened this issue Aug 20, 2019 · 14 comments
Closed

ActiveStorage::FileNotFoundError in before_save #36994

franktisellano opened this issue Aug 20, 2019 · 14 comments

Comments

@franktisellano
Copy link

franktisellano commented Aug 20, 2019

Steps to reproduce

Example Repo

Background

I'm using Active Storage with Disk storage. I want users to upload a json file attached to a model called Test_Model. The model has an attribute name that I'd like to populate with data from the JSON file.

When I call JSON.parse(self.jsonfile.download) to retrieve the name from the json file, I get an ActiveStorage::FileNotFoundError error.

Steps

  1. Clone the Example Repo
  2. bundle && rails db:migrate
  3. Visit http://localhost:3000/test_models/new
  4. Upload any file, leave Name blank

Expected behavior

I expect to be able to manipulate the as-yet-unsaved version of of this file before I save the record to the database.

Actual behavior

I get an ActiveStorage::FileNotFoundError error with the following exception cause: Errno::ENOENT: No such file or directory @ rb_sysopen - /Users/franktisellano/Desktop/activestorage-example/storage/i6/7e/i67ej7r9nx1yz838eor5v1hwpa3o.

System configuration

Rails version: 6.0.0

Ruby version: 2.5.0

Operating System: Mac 10.13.6 High Sierra

@franktisellano franktisellano changed the title ActiveStorage::FileNotFoundError on before_save ActiveStorage::FileNotFoundError in before_save Aug 20, 2019
@georgeclaghorn
Copy link
Contributor

This is the intended behavior (#33303). The file doesn’t get uploaded to storage until after the transaction commits. Download it after_commit instead:

class TestModel < ApplicationRecord
  has_one_attached :jsonfile
  after_commit :extract_name_from_json

  private
    def extract_name_from_json
      update! name: JSON.parse(jsonfile.download).fetch("name")
    end
end

@franktisellano
Copy link
Author

@georgeclaghorn I'll do that, but is there no way to manipulate the file in-memory before saving it to disk permanently?

@franktisellano
Copy link
Author

@georgeclaghorn That didn't work. I still receive the FileNotFound Error.

@ahmeij
Copy link

ahmeij commented Aug 21, 2019

I'm facing the same problem. I have a custom validator that checks the contents of an uploaded excel file.

After a bit of searching I found the file can be accessed before_save or during validations with the following code:

record.attachment_changes['<attributename>'].attachable

This is a reference to the Tempfile that will be uploaded to storage during the save of the record.

@franktisellano
Copy link
Author

franktisellano commented Aug 21, 2019 via email

@inopinatus
Copy link
Contributor

The fact we used to be able to work with the unsaved file, and now cannot, is a thoroughly irksome regression.

@sofianeOuafir
Copy link

any solution?

@franktisellano
Copy link
Author

any solution?

I ended up using File.read(self.attachment_changes['hlx_file'].attachable) to read the file using a before_create callback.

@mladenilic
Copy link

That didn't work. I still receive the FileNotFound Error.

Attachment is uploaded (stored on actual storage service) using after_commit callback.

Example that @georgeclaghorn provided will not work because after_ callbacks are executed in reverse order. This means that extract_name_from_json is executed before file is uploaded, hence the FileNotFound.

Here is the working example:

class TestModel < ApplicationRecord
  # Has to be called before has_one_attached
  after_commit :extract_name_from_json

  has_one_attached :jsonfile

  private
    def extract_name_from_json
      update! name: JSON.parse(jsonfile.download).fetch("name")
    end
end

@georgeclaghorn: What do you think about adding example of how to manipulate attachment with after_commit callback to the active storage guide? It might also be useful to add callback invoke order gotcha to the AR callback guide? I'd be happy to open PRs.

@ErikDeBruijn
Copy link

Changing the order fixed the problem, we can now finally upgrade to Rails 6 (see our issue here

@mihael
Copy link

mihael commented Dec 18, 2019

Same here, fixed by changing order.

@mladenilic hvala za fix!

@XaviBatis
Copy link

Guys! the link 'example repo' is not working any more, could you please fix it so we can have all details ?

I tried to build a web app from scratch following your tips but I was getting errors probably bc i had to improvise some code, it would be a way more easier if you can reupload the 'example repo' to figure out how to go through the whole thing.

Thanks in advance

@zavan
Copy link
Contributor

zavan commented Apr 21, 2021

@georgeclaghorn: What do you think about adding example of how to manipulate attachment with after_commit callback to the active storage guide? It might also be useful to add callback invoke order gotcha to the AR callback guide? I'd be happy to open PRs.

How about a set of callbacks for has_one_attached? Like:

has_one_attached :json_file, after_upload: ->(json_file) { update! name: JSON.parse(jsonfile.download).fetch("name") }
has_one_attached :another_file, before_upload: :sanity_check_file

def sanity_check_file(tempfile)
  # do whatever with tempfile, an instance of ActionDispatch::Http::UploadedFile
end

caiofct added a commit to hmatringe/conscients that referenced this issue Sep 16, 2021
…ia email to avoid getting a ActiveStorage::FileNotFoundError dues to transactional nature of ActiveStorage on Rails 6.

For more information see: rails/rails#36994
@pdeka
Copy link

pdeka commented Mar 9, 2022

Thanks for helping out. But can someone tell me why this was changed? i.e. we were able to work with files before committing in rails 5. What was the reason why this was taken away in rails 6.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests