Download & Extend

image_gd_save() does not support remote stream wrappers

Project:Drupal core
Version:7.x-dev
Component:image system
Category:bug report
Priority:normal
Assigned:Unassigned
Status:closed (fixed)

Issue Summary

The GD functions imagegif(), imagepng(), and imagejpeg() do not support remote stream wrappers (e.g. imagegif($im, "s3://foo/bar.gif") fails).

image_gd_save() contains a comment related to stream wrappers and uses $wrapper->realpath() to get the local filepath:

  // Convert URI to a normal path because PHP apparently has some gaps in stream wrapper support.
  if ($wrapper = file_stream_wrapper_get_instance_by_uri($destination)) {
    $destination = $wrapper->realpath();
  }

If $wrapper->realpath() returns a path that is not a filesystem path (is that allowed? The documentation for DrupalStreamWrapperInterface::realpath() isn't very explicit about the return value), the call to imagexxx() will fail.

The function needs to use a temporary path a suggested in one of the early patches for #227232: Support PHP stream wrappers, e.g. the one in comment #103.

Comments

#1

subscribe

#2

So - the suggested approach is to save to a temporary local file and then move/copy to a remote destination?

#3

Yes. Perhaps we can use some of the new stream wrapper flags that were just introduced in #685074: (Tests needed) Some stream wrappers need to be hidden or read-only.

I'm not sure I like how $wrapper->realpath() currently returns a path without wrapper prefix. This makes it difficult to normalize a path while preserving the wrapper prefix (e.g. "public://foo/bar/../baz.txt" to "public://foo/baz.txt"). Perhaps we should make the current getLocalPath() part of the DrupalLocalStreamWrapper interface and use that instead?

#4

Hmm, well we can use the local property I think.

#5

Status:active» needs review

totally untested...

AttachmentSizeStatusTest resultOperations
696150-gd-tempnam-5.patch1.99 KBIdleFAILED: [[SimpleTest]]: [MySQL] Unable to apply patch 696150-gd-tempnam-5.patch.View details

#6

shouldn't you use !isset($parents['DrupalLocalStreamWrapper']) rather than empty($parents['DrupalLocalStreamWrapper']) so it doesn't throw a warning if that's not set?

looks good otherwise.

#7

@aaron: the empty() check includes isset(), i.e. it doesn't throw an error if fed an uninitialized variable, so it is usually the most appropriate choice if some needed data is present...

#8

This would require alternate wrapper implementations to be subclass DrupalLocalStreamWrapper (it is an abstract class, not an interface). Would it be better to either introduce an interface or look for STREAM_WRAPPERS_LOCAL? Stream wrappers is a generic PHP feature, so a wrapper may be written for other uses than just in Drupal.

BTW whatever approach for detecting local files chosen here should also be applied to the top of archiver_get_archiver() (the logic currently in use there is flawed).

#9

#5: 696150-gd-tempnam-5.patch queued for re-testing.

#10

Status:needs review» needs work

The last submitted patch, 696150-gd-tempnam-5.patch, failed testing.

#11

Status:needs work» needs review

refactored to look for STREAM_WRAPPERS_LOCAL

AttachmentSizeStatusTest resultOperations
696150-gd-remote.patch2.67 KBIdlePASSED: [[SimpleTest]]: [MySQL] 24,643 pass(es).View details

#12

While I'm here, remove the unused $extension param from the docs. extension is part of the image object.

AttachmentSizeStatusTest resultOperations
696150-gd-remote.patch2.57 KBIdlePASSED: [[SimpleTest]]: [MySQL] 24,647 pass(es).View details

#13

Status:needs review» reviewed & tested by the community

Looks good to me.

#14

Status:reviewed & tested by the community» fixed

Committed to CVS HEAD. Thanks.

#15

Status:fixed» closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.