Interesting CDN/performance issue I have so I thought I'd bring it up.

I'm using the CDN module with CloudFront in origin pull mode. Basically that means that my site serves up all of its images with a URL like http://mycdn.amazon.com/picture.jpg (not http://mysite.com/picture.jpg), and then when the user hits amazon, CloudFront serves up a cached image. If Amazon doesn't have a cached image, they come and visit my site and have the imagecache generated and sent back first to Amazon and then the end user that requested it. Very straight-forward, very standard.

One of the issues I ran into initially with this method was that when a user on the Drupal site replaces an existing photo, CloudFront was serving up the previously cached version of it. The filename was the same, so Amazon had no way to know it changed (until it hits the CloudFront cache expiration), and I didn't want to send them a force-cache-expiration command because it takes 30 seconds to 1 minute or more to clear it out. My solution to this was to use the filefield_paths module, which I use to modify the filename of the uploaded image with tokens. One of my tokens is the md5 checksum of the current timestamp, so the resulting image name is myimage-6ada540363d39257b9b163ae857ef28d.jpg. So every time the user uploads a new file, filefield_paths renames the master image, and since the image is now using a new name all of the ImageCache presets are regenerated upon request and Amazon has no cache. It's like instant cache expiration and it works very well. The old images just time out from CloudFront.

OK so now on to the rub with this module. With imagecrop, now a derivative ImageCache preset can change without warning, and without the master filefield/imagefield changing. What happens is that a user goes in to the node edit page, creates a crop, the derivative ImageCache image is created, cached at the CDN, and it's done. But... the user doesn't like their initial crop, so they go to re-do it or make changes. The new ImageCache file has the same filename, so the version at the CDN is served back to the user and they get confused because it looks like the crop isn't working. It's because they get the old image from the CDN because the filename did not change.

What I need to have happen now, is that whenever a user creates a new crop setting - the master imagefield image is renamed according to my standard filefield_paths routine. That would then essentially flush out any remaining cached copies of the image at Amazon and to the user it looks like their change is taking effect right away. I'll take a shot at implementing this, and I'm hoping there is a hook somewhere in imagecrop so I can take some action when it happens. If not this will turn into a feature request for such a hook :)

I wrote a lot of stuff here, hopefully it makes sense. Thoughts/comments are welcome.

CommentFileSizeAuthor
#8 1250506-8.patch991 bytesrjbrown99

Comments

rjbrown99’s picture

Thinking more about this. It would seem the place to do this would be in imagecrop.module, create_image_object(). Specifically, before running an imagecache_build_derivative() to start creating new derived images.

    // if we have a dst, flush the image with all the needed imagecache actions.
    if (!empty($dst)) {
      file_delete($dst);
      imagecache_build_derivative($preset['actions'], $src, $dst);
    }

Perhaps adding a hook there? I haven't thought through all of the implications of this yet, nor have I fully explored how imagecrop uses the files within its UI.

nils.destoop’s picture

Yeah, adding a hook looks like a good idea. I'll create one for both D6 and D7 version.

nils.destoop’s picture

Status: Active » Needs review

I'v added "hook_imagecrop_settings_update" to both d6 and d7 version.

rjbrown99’s picture

Awesome, thank you. Seems like a good place to do it on the form submit action.

I'll roll up an example implementation of the hook for renaming of the file. I should have that done within the next few days, so if you like keep this open and I'll post my code back and close it out.

Thanks for the assistance and support!

rjbrown99’s picture

OK, here's some documentation - perhaps for a DEVELOPER.txt?

Imagecrop makes available the following developer hooks:

==============

hook_imagecrop_settings_update($form_values) 

This hook is run during the imageoffsets_form_submit. IE, when the imagecrop form is being saved with the new preset values. The new updated crop values are saved to the database, the existing preset is cleared, then this hook is run.

This is especially useful for scenarios where you may want to do things like rename the imagecaceh file. That use case is applicable to CDNs, where the previous filename may be cached upstream and there is a need to either expire the cached file or perhaps rename the file to prompt a CDN to re-cache it.

This is not perfect, but here's an implementation of the hook that uses filefield_paths to rename the file. In my case, my filefield_paths token inserts that custom timestamp/md5 hash so the files always get different names.

/**
 * Implementation of hook_imagecrop_settings_update()
 */
function mymodule_imagecrop_settings_update($form_values) {
  if (is_numeric($form_values['fid'])) {
    // Load up the file
    $file = field_file_load($form_values['fid']);
    // We don't get NID in the $form_values, so we need to go look up what node this belongs to
    $references = array_keys(filefield_get_file_references($file, $form_values['field']));
    // Not sure what to do if more than one node comes back, or even how that would happen so I lock this at 1. Works for me
    if (count($references) == 1) {
      $node = node_load($references[0]); 
      // This forcibly sets a filefield_paths variable to ensure the file is renamed. 
      $resetvar = "ffp_" . $node->type . "_" . $form_values['field'];
      variable_set($resetvar, TRUE);
      // Rename the file with filefield_paths
      filefield_paths_node_update($node);
      // Kill the var again so other renames don't happen for other parts of the site
      variable_del($resetvar);
    }
  }
}
rjbrown99’s picture

Title: CDN support » CDN support, new developer hook upon saving crop settings
Status: Needs review » Fixed

I declare this fixed! :) Thanks for the hook, very helpful.

rjbrown99’s picture

Status: Fixed » Needs work

OK it's not really fixed :(

I think the hook is in the wrong place. It works in the form submit action for the final photo, but it does not work when the user is actively resizing/rescaling images in the viewport. IE, I select a dropdown and choose a different size for the photo but before I save it. When that happens, the js callback hits imagecrop_docrop() only and not the submit. The end result is that the image inside of the viewport is not updated because the CDN is still serving the same cached name of the image.

If the hook is in imagecrop_docrop() that would seem to fix it, because it would handle both the submit as well as any realtime changes by the user when they fiddle with the scale dropdown.

rjbrown99’s picture

StatusFileSize
new991 bytes

Here's a patch for 6.x. It moves the hook to imagecrop_docrop, which now seems to work for me - at least so far.

rjbrown99’s picture

Status: Needs work » Needs review

Yup, that works for me. Here's a revised version of my hook implementation:

/**
 * Implementation of hook_imagecrop_settings_update()
 *   This hook is called on the form submit action for the imagecrop module. When the user selects a new
 *   crop, this hook provides a bunch of values related to the new crop. We need to call this to
 *   change the master image filename. This effectively 'clears' the upstream CDN image cache. Since
 *   the name of the image changed, all imagecache presets are regenerated and re-cached. This ensures
 *   that the changed preset is shown immediately to the user.
 */
function mymodule_imagecrop_settings_update($fid, $presetid, $module, $field, $node_type) {
  if (is_numeric($fid)) {
    $file = field_file_load($fid);
    $references = array_keys(filefield_get_file_references($file, $field));
    if (count($references) == 1) {
      $node = node_load($references[0]);
      $resetvar = "ffp_" . $node_type . "_" . $field;
      variable_set($resetvar, TRUE);
      filefield_paths_node_update($node);
      variable_del($resetvar);
    }
  }
}
rjbrown99’s picture

Still working this...

Right now, with the above patch, everything works properly when you are in the crop popup box. The image is renamed, imagecache created, cached at the CDN, boom looks good. The problem is that when the actual node is saved/submitted (not the crop - the actual node save), I have another function which triggers a regeneration of the image by using imagecache_generate_image(). The issue with this is that the image is NOT created using imagecrop, or at least it doesn't work properly. It generates an image of the correct size, with no cropping or scaling applied. Hmm.

wim leers’s picture

Category: support » feature

Subscribing.

It seems this has become a feature request by now… :)

nils.destoop’s picture

The problem with imagecache_generate_image(), is that ' imagecache_build_derivative' doesn't give any information to the preset actions, what preset is currently beïng saved.

Because of this, imagecrop needs to look at the current url, to strip out the requested preset. When only imagecache_build_derivative is beïng called, no imagecache url is beïng requested.

Maybe i should take a look for a patch for both imagecache (d6) and core (d7)

bradjones1’s picture

Version: 6.x-1.x-dev » 7.x-1.x-dev
Status: Needs review » Needs work

Bumping this to 7.x to gather some attention to this very useful proposal. I basically have the same use-case as explained above, without having pit-stopped at filefield_paths. Last update is 4 years ago so if the 6.x patch isn't getting much love, let's try for 7.x.

jennypanighetti’s picture

I too would like a hook for when the new cropped image is saved. @bradjones1, do you have a workaround for this?

bradjones1’s picture

This is on my list of things to do for a personal project but it hasn't happened yet... I think it would be rather straightforward, though. If/when I get to it, I'll post a patch.

I'm also open to being hired to work on this, depending on your urgency.

jennypanighetti’s picture

I actually found another issue's comment (can't remember where) and found hook_imagecrop_settings_update($record). I could get the info I need from $record and perform the action I needed.