verbb / image-resizer

A Craft CMS plugin to resize images on upload.
MIT License
128 stars 14 forks source link

[2.2.2] AssetException: Could not open create the stream for {filename.ext} #81

Closed mnlmaier closed 9 months ago

mnlmaier commented 10 months ago

Describe the bug

Since 2.2.2, handling assets in AWS seems to be broken. I can easily and consistently reproduce this when trying to edit an image which was uploaded in a S3 bucket. The upload itself works fine, but interacting / updating the image throws the following error:

craft\errors\AssetException: Could not open create the stream for “guillaume-galtier-3YrppYQPoCI-unsplash.jpg”

I used this JPG as a reference, though it happens with all images which were uploaded, even with SVGs.

Up until 2.2.1, it all was working fine, so i highly suspect this commit being the culprit: https://github.com/verbb/image-resizer/commit/743562c14bcfdcb4b4da6b4735a303a4be6a171d

For the time being, I've downgraded to 2.2.1, though I wanted to let you know, since this might be easy to pinpoint.


(Note: I have redacted the project path from the stack trace)

<?php
craft\errors\AssetException: Could not open create the stream for “guillaume-galtier-3YrppYQPoCI-unsplash.jpg” in {redacted-path-to-craft-root}vendor/craftcms/cms/src/base/FlysystemVolume.php:189
Stack trace:
#0 {redacted-path-to-craft-root}vendor/craftcms/cms/src/base/FlysystemVolume.php(200): craft\base\FlysystemVolume->getFileStream('guillaume-galti...')
#1 {redacted-path-to-craft-root}vendor/verbb/image-resizer/src/services/Resize.php(51): craft\base\FlysystemVolume->saveFileLocally('guillaume-galti...', '/Users/manuel/C...')
#2 {redacted-path-to-craft-root}vendor/verbb/image-resizer/src/services/Service.php(48): verbb\imageresizer\services\Resize->resize(Object(craft\elements\Asset), 'guillaume-galti...', '/Users/manuel/C...')
#3 [internal function]: verbb\imageresizer\services\Service->beforeHandleAssetFile(Object(craft\events\AssetEvent))
#4 {redacted-path-to-craft-root}vendor/yiisoft/yii2/base/Event.php(312): call_user_func(Array, Object(craft\events\AssetEvent))
#5 {redacted-path-to-craft-root}vendor/yiisoft/yii2/base/Component.php(642): yii\base\Event::trigger('craft\\elements\\...', 'beforeHandleFil...', Object(craft\events\AssetEvent))
#6 {redacted-path-to-craft-root}vendor/craftcms/cms/src/elements/Asset.php(2367): yii\base\Component->trigger('beforeHandleFil...', Object(craft\events\AssetEvent))
#7 {redacted-path-to-craft-root}vendor/craftcms/cms/src/services/Elements.php(2531): craft\elements\Asset->beforeSave(false)
#8 {redacted-path-to-craft-root}vendor/craftcms/cms/src/services/Elements.php(785): craft\services\Elements->_saveElementInternal(Object(craft\elements\Asset), true, true, NULL)
#9 {redacted-path-to-craft-root}vendor/craftcms/cms/src/controllers/AssetsController.php(285): craft\services\Elements->saveElement(Object(craft\elements\Asset))
#10 [internal function]: craft\controllers\AssetsController->actionSaveAsset()
#11 {redacted-path-to-craft-root}vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#12 {redacted-path-to-craft-root}vendor/yiisoft/yii2/base/Controller.php(178): yii\base\InlineAction->runWithParams(Array)
#13 {redacted-path-to-craft-root}vendor/yiisoft/yii2/base/Module.php(552): yii\base\Controller->runAction('save-asset', Array)
#14 {redacted-path-to-craft-root}vendor/craftcms/cms/src/web/Application.php(295): yii\base\Module->runAction('assets/save-ass...', Array)
#15 {redacted-path-to-craft-root}vendor/craftcms/cms/src/web/Application.php(608): craft\web\Application->runAction('assets/save-ass...', Array)
#16 {redacted-path-to-craft-root}vendor/craftcms/cms/src/web/Application.php(274): craft\web\Application->_processActionRequest(Object(craft\web\Request))
#17 {redacted-path-to-craft-root}vendor/yiisoft/yii2/base/Application.php(384): craft\web\Application->handleRequest(Object(craft\web\Request))
#18 {redacted-path-to-craft-root}web/index.php(21): yii\base\Application->run()
#19 /Applications/Herd.app/Contents/Resources/valet/server.php(120): require('/Users/manuel/C...')
#20 {main}

Steps to reproduce

  1. Upload an asset in a S3 bucket
  2. Open the asset
  3. Try to update its properties (e.g. the title)
  4. Notice the exception

Craft CMS version

3.9.5

Plugin version

2.2.2

Multi-site?

Yes

Additional context

Happens in all environments, working fine when downgrading image-resizer to v2.2.1

engram-design commented 10 months ago

When you say interacting / updating the image throws the error - what sort of actions are you doing? I've been testing with the image editor, and even replacing the image and of course editing the title like you mention, and things seem okay to me!

mnlmaier commented 10 months ago

@engram-design What made it reproducible for me consistently was:

  1. Opening the Asset Management in the Craft Backend
  2. Selecting an Asset Volume which is not local (in our case, AWS S3)
  3. Uploading an Image (or select an already uploaded Asset), while uploading itself is working fine
  4. Opening the image 5a. Editing the title OR 5b. Editing a custom text field we added (additional field for image credits)
  5. Saving the image leads to the exception

When downgrading to 2.2.1 without adjusting other dependencies, the error disappears. When upgrading again, the error re-appears.

Same thing happens when trying to save a new image to a global asset field in a multi site environment:

  1. Opening the globals
  2. Selecting an image (already uploaded or newly uploaded) for the Asset Field (with assets stored in S3)
  3. Trying to save the Globals leads to the same exception

I know it might be really hard trying to pinpoint the exact issue… Is there any additional information which I could provide? Happy to do so!

engram-design commented 10 months ago

Thanks for all the clarifications! Still struggling to reproduce that, but I'll keep at it! I'd rather not blindly change things back unless I can verify the original fix is working.

FYI, we made some changes to handle non-local volumes and how images are processed. Specifically, we ensure that saveFileLocally() is called, just in case there's not a local (albeit temporary) image for us to resize and replace on the remote volume. Normally, Craft will handle this itself for things like transforms, but errors were creeping in where the cached local file didn't exist. Just don't want to mess things up with those fixes.

And just to make sure I'm not going insane - here's what I'm doing - https://share.cleanshot.com/99W14G51

jamiepittock commented 9 months ago

We're noticing this too when moving assets between folders. It wasn't happening every time but I think I've narrowed it down. If I move an asset the first time it works fine. If I try to move the same asset after that, I get the exception mentioned above.

Here's a (poor) recording attempting to show you. I move the image into a subfolder, no problem. I then try to move it back again and I get the error.

https://www.dropbox.com/scl/fi/imenr768ovgh4u6jz2yyg/recording.mov?rlkey=7lqa6h36axy40va77txbutez6&dl=0

jamiepittock commented 9 months ago

In addition...

Downgrading to 2.2.1 didn't help for us. It resolved the issue but it popped up elsewhere. Using 2.2.1 we get the same exception but this time as the cause of a failed queue job.

This change does seem to work for us. I have no idea why or what the difference is, especially because getTransformSource() has been deprecated in Craft 4.

$path = $asset->tempFilePath ?? $asset->getTransformSource() ?? $asset->getImageTransformSourcePath();

I can understand your reluctance to mess with things. This has been tricky to debug across our different environments.

engram-design commented 9 months ago

Happy to employ that approach for now. Should be fixed in 2.2.3