summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormongolito4042012-03-23 21:53:36 (GMT)
committer Dave Reid2012-03-23 21:53:36 (GMT)
commitd8975331aa752a229e40d1203e5226ed530f1e04 (patch)
tree926ac8ca95cc1bdb4b228182bd8aad96004d5fdb
parent0a16da6854f78244be5884cfc9f492deb326234b (diff)
Issue #1447790: Fixed performance issues by not storing dimensions of image files.
-rw-r--r--file_entity.file.inc85
-rw-r--r--file_entity.install72
-rw-r--r--file_entity.module10
-rw-r--r--tests/file_entity.test46
4 files changed, 207 insertions, 6 deletions
diff --git a/file_entity.file.inc b/file_entity.file.inc
index 9921b37..6f50207 100644
--- a/file_entity.file.inc
+++ b/file_entity.file.inc
@@ -32,6 +32,9 @@ function file_entity_file_insert($file) {
// Clear the page and block caches.
cache_clear_all();
+
+ // Get and store image dimensions.
+ file_entity_image_dimensions($file, TRUE);
}
/**
@@ -46,6 +49,9 @@ function file_entity_file_update($file) {
// Clear the page and block caches.
cache_clear_all();
+
+ // Get and store image dimensions.
+ file_entity_image_dimensions($file, TRUE);
}
/**
@@ -60,6 +66,9 @@ function file_entity_file_delete($file) {
// Clear the page and block caches.
cache_clear_all();
+
+ // Delete image dimensions from the {image_dimensions} table
+ db_query('DELETE FROM {image_dimensions} WHERE fid = :fid', array(':fid' => $file->fid));
}
/**
@@ -109,3 +118,79 @@ function file_entity_file_operation_info() {
return $info;
}
+
+/**
+ * Implements hook_file_load().
+ */
+function file_entity_file_load($files) {
+ // Load images dimensions already in the {image_dimensions} table.
+ $result = db_query('SELECT * FROM {image_dimensions} id WHERE id.fid IN (:fids)', array(':fids' => array_keys($files)));
+ foreach ($result as $record) {
+ $files[$record->fid]->image_dimensions = array(
+ 'width' => $record->width,
+ 'height' => $record->height,
+ );
+ }
+ // Retrieve any missing images dimensions.
+ foreach ($files as $file) {
+ file_entity_image_dimensions($file, FALSE);
+ }
+}
+
+/**
+ * Retrieve the dimensions of an image file and store them in the
+ * {image dimensions} table.
+ *
+ * @param $file
+ * A file object.
+ *
+ * @param $force
+ * TRUE if the image dimensions should always be loaded from the actual file
+ * even if $file->image_dimensions is already set.
+ *
+ * @return
+ * The image dimensions as an array with the 'width' and 'height' properties.
+ * The array is also added to $file as its image_dimensions property. If the
+ * image dimensions cannot be read, the 'width' and 'height' properties will
+ * be NULL. If $file is either empty or not an image file, FALSE is returned.
+ */
+function file_entity_image_dimensions($file, $force = FALSE) {
+ // Prevent PHP notices when trying to read empty files.
+ // @see http://drupal.org/node/681042
+ if (!filesize($file->uri)) {
+ return;
+ }
+
+ // Do not bother proceeding if this file does not have an image mime type.
+ if (strpos($file->filemime, 'image/') !== 0) {
+ return;
+ }
+
+ // Return the existing $file->image_dimensions unless a reload is forced.
+ if (!$force && isset($file->image_dimensions)) {
+ return $file->image_dimensions;
+ }
+
+ // We have a non-empty image file.
+ $image_info = image_get_info($file->uri);
+ if ($image_info) {
+ $file->image_dimensions = array(
+ 'width' => $image_info['width'],
+ 'height' => $image_info['height'],
+ );
+ db_merge('image_dimensions')
+ ->key(array('fid' => $file->fid))
+ ->fields(array(
+ 'width' => $file->image_dimensions['width'],
+ 'height' => $file->image_dimensions['height'],
+ ))
+ ->execute();
+ }
+ else {
+ // Fallback to NULL values.
+ $file->image_dimensions = array(
+ 'width' => NULL,
+ 'height' => NULL,
+ );
+ }
+}
diff --git a/file_entity.install b/file_entity.install
index 8fecc7b..305b80b 100644
--- a/file_entity.install
+++ b/file_entity.install
@@ -63,7 +63,38 @@ function file_entity_schema() {
),
),
);
-
+ $schema['image_dimensions'] = array(
+ 'description' => 'Cache images dimensions.',
+ 'fields' => array(
+ 'fid' => array(
+ 'description' => 'File ID.',
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'height' => array(
+ 'description' => 'The height of the image in pixels.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'width' => array(
+ 'description' => 'The width of the image in pixels..',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'primary key' => array('fid'),
+ 'foreign keys' => array(
+ 'file_managed' => array(
+ 'table' => 'file_managed',
+ 'columns' => array('fid' => 'fid'),
+ ),
+ ),
+ );
return $schema;
}
@@ -247,3 +278,42 @@ function file_entity_update_7104() {
}
}
}
+
+/**
+ * Create the {image_dimensions} database table.
+ */
+function file_entity_update_7200() {
+ $schema['image_dimensions'] = array(
+ 'description' => 'Cache images dimensions.',
+ 'fields' => array(
+ 'fid' => array(
+ 'description' => 'File ID.',
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'height' => array(
+ 'description' => 'The height of the image in pixels.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'width' => array(
+ 'description' => 'The width of the image in pixels..',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'primary key' => array('fid'),
+ 'foreign keys' => array(
+ 'file_managed' => array(
+ 'table' => 'file_managed',
+ 'columns' => array('fid' => 'fid'),
+ ),
+ ),
+ );
+ db_create_table('image_dimensions', $schema['image_dimensions']);
+}
diff --git a/file_entity.module b/file_entity.module
index effb571..8d2e31c 100644
--- a/file_entity.module
+++ b/file_entity.module
@@ -531,22 +531,22 @@ function file_entity_file_formatter_file_image_view($file, $display, $langcode)
return;
}
- if (file_entity_file_is_local($file) && $image = image_load($file->uri)) {
+ if (file_entity_file_is_local($file) && isset($file->image_dimensions)) {
if (!empty($display['settings']['image_style'])) {
$element = array(
'#theme' => 'image_style',
'#style_name' => $display['settings']['image_style'],
'#path' => $file->uri,
- '#width' => $image->info['width'],
- '#height' => $image->info['height'],
+ '#width' => $file->image_dimensions['width'],
+ '#height' => $file->image_dimensions['height'],
);
}
else {
$element = array(
'#theme' => 'image',
'#path' => $file->uri,
- '#width' => $image->info['width'],
- '#height' => $image->info['height'],
+ '#width' => $file->image_dimensions['width'],
+ '#height' => $file->image_dimensions['height'],
);
}
return $element;
diff --git a/tests/file_entity.test b/tests/file_entity.test
index 94abcea..6401743 100644
--- a/tests/file_entity.test
+++ b/tests/file_entity.test
@@ -61,6 +61,52 @@ class FileEntityUnitTestCase extends FileEntityTestHelper {
$uri = entity_uri('file', $file);
$this->assertEqual($uri['path'], "file/{$file->fid}");
}
+
+ function testImageDimensions() {
+ $images_dimensions = array();
+ $text_fids = array();
+ // Test hook_file_insert().
+ // Files have been saved as part of setup (in FileEntityTestHelper::setUpFiles).
+ foreach ($this->files['image'] as $file) {
+ $images_dimensions[$file->fid] = $file->image_dimensions;
+ $this->assertTrue(isset($file->image_dimensions), 'Image dimensions retrieved on file_save() for an image file.');
+ }
+ foreach ($this->files['text'] as $file) {
+ $text_fids[] = $file->fid;
+ $this->assertFalse(isset($file->image_dimensions), 'No image dimensions retrieved on file_save() for an text file.');
+ }
+
+ // Test hook_file_load().
+ // Clear the cache and load fresh files objects to test file_load behavior.
+ entity_get_controller('file')->resetCache();
+ foreach (file_load_multiple(array_keys($images_dimensions)) as $file) {
+ $this->assertTrue(isset($file->image_dimensions), 'Image dimensions retrieved on file_load() for an image file.');
+ $this->assertEqual($file->image_dimensions['height'], $images_dimensions[$file->fid]['height'], 'Loaded image height is equal to saved image height.');
+ $this->assertEqual($file->image_dimensions['width'], $images_dimensions[$file->fid]['width'], 'Loaded image width is equal to saved image width.');
+ }
+ foreach (file_load_multiple($text_fids) as $file) {
+ $this->assertFalse(isset($file->image_dimensions), 'No image dimensions retrieved on file_load() for an text file.');
+ }
+
+ // Test hook_file_update().
+ // Load the first image file and resize it.
+ $file = file_load(reset(array_keys($images_dimensions)));
+ $image = image_load($file->uri);
+ image_resize($image, $file->image_dimensions['width'] / 2, $file->image_dimensions['height'] / 2);
+ image_save($image);
+ file_save($file);
+ $this->assertEqual($file->image_dimensions['height'], $images_dimensions[$file->fid]['height'] / 2, 'Image file height updated by file_save().');
+ $this->assertEqual($file->image_dimensions['width'], $images_dimensions[$file->fid]['width'] / 2, 'Image file width updated by file_save().');
+ // Clear the cache and reload the file.
+ entity_get_controller('file')->resetCache();
+ $file = file_load($file->fid);
+ $this->assertEqual($file->image_dimensions['height'], $images_dimensions[$file->fid]['height'] / 2, 'Updated image height retrieved by file_load().');
+ $this->assertEqual($file->image_dimensions['width'], $images_dimensions[$file->fid]['width'] / 2, 'Updated image width retrieved by file_load().');
+
+ //Test hook_file_delete().
+ file_delete($file, TRUE);
+ $this->assertFalse(db_query('SELECT count(*) FROM {image_dimensions} WHERE fid = :fid', array(':fid' => 'fid'))->fetchField(), 'Row deleted in {file_dimensions} on file_delete().');
+ }
}
class FileEntityTokenTestCase extends FileEntityTestHelper {