Browse Source

Merge pull request #259 from MissAllSunday/avatar_optimization

Avatar optimization
emanuele45 12 years ago
parent
commit
0ccec5943e
5 changed files with 219 additions and 49 deletions
  1. 39 1
      .gitignore
  2. 141 0
      Sources/Avatar.php
  3. 20 38
      Sources/Display.php
  4. 1 0
      Themes/default/languages/Errors.english.php
  5. 18 10
      index.php

+ 39 - 1
.gitignore

@@ -6,4 +6,42 @@ Packages/backups/*
 Packages/temp
 *.*~
 
-.DS_Store
+# Compiled source #
+###################
+*.com
+*.class
+*.dll
+*.exe
+*.o
+*.so
+*.bat
+*.session
+# Packages #
+############
+# it's better to unpack these files and commit the raw source
+# git has its own built in compression methods
+*.7z
+*.dmg
+*.gz
+*.iso
+*.jar
+*.rar
+*.tar
+*.zip
+
+# Logs and databases #
+######################
+*.log
+*.sql
+*.sqlite
+*.session
+
+# OS generated files #
+######################
+.DS_Store*
+ehthumbs.db
+Icon?
+Thumbs.db
+*.lnk
+/nbproject/private/
+/nbproject/

+ 141 - 0
Sources/Avatar.php

@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * This file handles the avatar requests. The whole point of this file is to reduce the loaded stuff to show an image
+ *
+ * Simple Machines Forum (SMF)
+ *
+ * @package SMF
+ * @author Simple Machines
+ *
+ * @copyright 2012 Simple Machines
+ * @license http://www.simplemachines.org/about/smf/license.php BSD
+ *
+ * @version 2.1 Alpha 1
+ */
+
+if (!defined('SMF'))
+	die('Hacking attempt...');
+
+function showAvatar()
+{
+	global $smcFunc, $modSettings, $sourcedir, $maintenance;
+
+	// We need a valid ID
+	if(empty($_GET['attach']) || (string)$_GET['attach'] != (string)(int)$_GET['attach'])
+		die;
+
+	// No access in strict maintenance mode
+	if(!empty($maintenance) && $maintenance == 2)
+		die;
+
+	// This is done to clear any output that was made before now.
+	if(!empty($modSettings['enableCompressedOutput']) && !headers_sent() && ob_get_length() == 0)
+	{
+		if(@ini_get('zlib.output_compression') == '1' || @ini_get('output_handler') == 'ob_gzhandler')
+			$modSettings['enableCompressedOutput'] = 0;
+		else
+			ob_start('ob_gzhandler');
+	}
+
+	if(empty($modSettings['enableCompressedOutput']))
+	{
+		ob_start();
+		header('Content-Encoding: none');
+	}
+
+	// Better handling
+	$id_attach = (int) $_GET['attach'];
+
+	// Use cache when possible
+	if(($cache = cache_get_data('avatar_lookup_id-'. $id_attach)) != null)
+		$file = $cache;
+
+	// Get the info from the DB
+	else
+	{
+		$request = $smcFunc['db_query']('', '
+			SELECT id_folder, filename AS real_filename, file_hash, fileext, id_attach, attachment_type, mime_type, approved, id_member
+			FROM {db_prefix}attachments
+			WHERE id_attach = {int:id_attach}
+				AND id_member > {int:blank_id_member}
+			LIMIT 1',
+			array(
+				'id_attach' => $id_attach,
+				'blank_id_member' => 0,
+			)
+		);
+
+		$file = $smcFunc['db_fetch_assoc']($request);
+
+		// Update the download counter (unless it's a thumbnail).
+		if ($file['attachment_type'] != 3)
+			$smcFunc['db_query']('attach_download_increase', '
+				UPDATE LOW_PRIORITY {db_prefix}attachments
+				SET downloads = downloads + 1
+				WHERE id_attach = {int:id_attach}',
+				array(
+					'id_attach' => $id_attach,
+				)
+			);
+
+		$file['filename'] = getAttachmentFilename($file['real_filename'], $id_attach, $file['id_folder'], false, $file['file_hash']);
+
+		// ETag time
+		$file['etag'] = '"'. function_exists('md5_file') ? md5_file($file['filename']) : md5(file_get_contents($file['filename'])). '"';
+
+		// Cache it... (Why do I randomly select a length at which to expire? Search around for RIP_JITTER :P)
+		cache_put_data('avatar_lookup_id-'. $id_attach, $file, mt_rand(850, 900));
+	}
+
+	// The file does not exists
+	if(!file_exists($file['filename']))
+	{
+		header('HTTP/1.0 404 File Not Found');
+		die('404 File Not Found');
+	}
+
+	// If it hasn't been modified since the last time this attachement was retrieved, there's no need to display it again.
+	if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']))
+	{
+		list($modified_since) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE']);
+		if (strtotime($modified_since) >= filemtime($file['filename']))
+		{
+			ob_end_clean();
+
+			// Answer the question - no, it hasn't been modified ;).
+			header('HTTP/1.1 304 Not Modified');
+			exit;
+		}
+	}
+
+	header('Pragma: ');
+	header('Expires: '. gmdate('D, d M Y H:i:s', time() + 31536000). ' GMT');
+	header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($file['filename'])). ' GMT');
+	header('Accept-Ranges: bytes');
+	header('Connection: close');
+	header('ETag: '. $file['etag']);
+	header('Content-Type: '. $file['mime_type']);
+
+	// Since we don't do output compression for files this large...
+	if (filesize($file['filename']) > 4194304)
+	{
+		// Forcibly end any output buffering going on.
+		while (@ob_get_level() > 0)
+			@ob_end_clean();
+
+		$fp = fopen($file['filename'], 'rb');
+		while (!feof($fp))
+		{
+			print fread($fp, 8192);
+			flush();
+		}
+		fclose($fp);
+	}
+
+	// On some of the less-bright hosts, readfile() is disabled.  It's just a faster, more byte safe, version of what's in the if.
+	elseif (@readfile($file['filename']) === null)
+		print file_get_contents($file['filename']);
+
+	die();
+}

+ 20 - 38
Sources/Display.php

@@ -1306,12 +1306,12 @@ function prepareDisplayContext($reset = false)
 }
 
 /**
- * Downloads an attachment or avatar, and increments the download count.
- * It requires the view_attachments permission. (not for avatars!)
+ * Downloads an attachment, and increments the download count.
+ * It requires the view_attachments permission.
  * It disables the session parser, and clears any previous output.
  * It depends on the attachmentUploadDir setting being correct.
  * It is accessed via the query string ?action=dlattach.
- * Views to attachments and avatars do not increase hits and are not logged in the "Who's Online" log.
+ * Views to attachments do not increase hits and are not logged in the "Who's Online" log.
  */
 function Download()
 {
@@ -1328,42 +1328,24 @@ function Download()
 
 	$_REQUEST['attach'] = isset($_REQUEST['attach']) ? (int) $_REQUEST['attach'] : (int) $_REQUEST['id'];
 
-	if (isset($_REQUEST['type']) && $_REQUEST['type'] == 'avatar')
-	{
-		$request = $smcFunc['db_query']('', '
-			SELECT id_folder, filename, file_hash, fileext, id_attach, attachment_type, mime_type, approved, id_member
-			FROM {db_prefix}attachments
-			WHERE id_attach = {int:id_attach}
-				AND id_member > {int:blank_id_member}
-			LIMIT 1',
-			array(
-				'id_attach' => $_REQUEST['attach'],
-				'blank_id_member' => 0,
-			)
-		);
-		$_REQUEST['image'] = true;
-	}
-	// This is just a regular attachment...
-	else
-	{
-		// This checks only the current board for $board/$topic's permissions.
-		isAllowedTo('view_attachments');
+	// This checks only the current board for $board/$topic's permissions.
+	isAllowedTo('view_attachments');
+
+	// Make sure this attachment is on this board.
+	// @todo: We must verify that $topic is the attachment's topic, or else the permission check above is broken.
+	$request = $smcFunc['db_query']('', '
+		SELECT a.id_folder, a.filename, a.file_hash, a.fileext, a.id_attach, a.attachment_type, a.mime_type, a.approved, m.id_member
+		FROM {db_prefix}attachments AS a
+			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg AND m.id_topic = {int:current_topic})
+			INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
+		WHERE a.id_attach = {int:attach}
+		LIMIT 1',
+		array(
+			'attach' => $_REQUEST['attach'],
+			'current_topic' => $topic,
+		)
+	);
 
-		// Make sure this attachment is on this board.
-		// @todo: We must verify that $topic is the attachment's topic, or else the permission check above is broken.
-		$request = $smcFunc['db_query']('', '
-			SELECT a.id_folder, a.filename, a.file_hash, a.fileext, a.id_attach, a.attachment_type, a.mime_type, a.approved, m.id_member
-			FROM {db_prefix}attachments AS a
-				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg AND m.id_topic = {int:current_topic})
-				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
-			WHERE a.id_attach = {int:attach}
-			LIMIT 1',
-			array(
-				'attach' => $_REQUEST['attach'],
-				'current_topic' => $topic,
-			)
-		);
-	}
 	if ($smcFunc['db_num_rows']($request) == 0)
 		fatal_lang_error('no_access', false);
 	list ($id_folder, $real_filename, $file_hash, $file_ext, $id_attach, $attachment_type, $mime_type, $is_approved, $id_member) = $smcFunc['db_fetch_row']($request);

+ 1 - 0
Themes/default/languages/Errors.english.php

@@ -25,6 +25,7 @@ $txt['not_a_user'] = 'The user whose profile you are trying to view does not exi
 $txt['not_a_topic'] = 'This topic doesn\'t exist on this board.';
 $txt['not_approved_topic'] = 'This topic has not been approved yet.';
 $txt['email_in_use'] = 'That email address (%1$s) is being used by a registered member already. If you feel this is a mistake, go to the login page and use the password reminder with that address.';
+$txt['attachments_no_write'] = 'The attachments directory is not writable';
 
 $txt['didnt_select_vote'] = 'You didn\'t select a vote option.';
 $txt['poll_error'] = 'Either that poll doesn\'t exist, the poll has been locked, or you tried to vote twice.';

+ 18 - 10
index.php

@@ -44,19 +44,10 @@ require_once(dirname(__FILE__) . '/Settings.php');
 if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
 	$cachedir = $boarddir . '/cache';
 
-// And important includes.
+// Without those we can't go anywhere
 require_once($sourcedir . '/QueryString.php');
-require_once($sourcedir . '/Session.php');
 require_once($sourcedir . '/Subs.php');
-require_once($sourcedir . '/Errors.php');
-require_once($sourcedir . '/Logging.php');
 require_once($sourcedir . '/Load.php');
-require_once($sourcedir . '/Security.php');
-require_once($sourcedir . '/Class-BrowserDetect.php');
-
-// Using an pre-PHP 5.1 version?
-if (version_compare(PHP_VERSION, '5.1', '<'))
-	require_once($sourcedir . '/Subs-Compat.php');
 
 // If $maintenance is set specifically to 2, then we're upgrading or something.
 if (!empty($maintenance) && $maintenance == 2)
@@ -84,6 +75,23 @@ if (isset($_GET['scheduled']))
 	require_once($sourcedir . '/ScheduledTasks.php');
 	AutoTask();
 }
+// Displaying attached avatars
+elseif (isset($_GET['action']) && $_GET['action'] == 'dlattach' && isset($_GET['type']) && $_GET['type'] == 'avatar')
+{
+	require_once($sourcedir. '/Avatar.php');
+	showAvatar();
+}
+
+// And important includes.
+require_once($sourcedir . '/Session.php');
+require_once($sourcedir . '/Errors.php');
+require_once($sourcedir . '/Logging.php');
+require_once($sourcedir . '/Security.php');
+require_once($sourcedir . '/Class-BrowserDetect.php');
+
+// Using an pre-PHP 5.1 version?
+if (version_compare(PHP_VERSION, '5.1', '<'))
+	require_once($sourcedir . '/Subs-Compat.php');
 
 // Check if compressed output is enabled, supported, and not already being done.
 if (!empty($modSettings['enableCompressedOutput']) && !headers_sent())