Relatively experimental change to store thumbnails in 'file/thumb/' (by default)

This commit is contained in:
Mikael Nordfeldth 2016-03-07 22:33:34 +01:00
parent a3b265a477
commit 265fa12917
9 changed files with 118 additions and 31 deletions

View File

@ -96,7 +96,7 @@ class AttachmentAction extends ManagedAction
{ {
if (empty($this->attachment->filename)) { if (empty($this->attachment->filename)) {
// if it's not a local file, gtfo // if it's not a local file, gtfo
common_redirect($this->attachment->url, 303); common_redirect($this->attachment->getUrl(), 303);
} }
parent::showPage(); parent::showPage();

View File

@ -62,6 +62,6 @@ class Attachment_thumbnailAction extends AttachmentAction
common_redirect($e->file->getUrl(), 302); common_redirect($e->file->getUrl(), 302);
} }
common_redirect(File_thumbnail::url($thumbnail->filename), 302); common_redirect(File_thumbnail::url($thumbnail->getFilename()), 302);
} }
} }

View File

@ -500,7 +500,7 @@ class File extends Managed_DataObject
{ {
if ($prefer_local && !empty($this->filename)) { if ($prefer_local && !empty($this->filename)) {
// A locally stored file, so let's generate a URL for our instance. // A locally stored file, so let's generate a URL for our instance.
return self::url($this->filename); return self::url($this->getFilename());
} }
// No local filename available, return the URL we have stored // No local filename available, return the URL we have stored

View File

@ -129,23 +129,76 @@ class File_thumbnail extends Managed_DataObject
static function path($filename) static function path($filename)
{ {
// TODO: Store thumbnails in their own directory and don't use File::path here if (!File::validFilename($filename)) {
return File::path($filename); // TRANS: Client exception thrown if a file upload does not have a valid name.
throw new ClientException(_('Invalid filename.'));
}
$dir = common_config('thumbnail', 'dir') ?: File::path('thumb');
if (!in_array($dir[mb_strlen($dir)-1], ['/', '\\'])) {
$dir .= DIRECTORY_SEPARATOR;
}
return $dir . $filename;
} }
static function url($filename) static function url($filename)
{ {
// TODO: Store thumbnails in their own directory and don't use File::url here if (!File::validFilename($filename)) {
return File::url($filename); // TRANS: Client exception thrown if a file upload does not have a valid name.
throw new ClientException(_('Invalid filename.'));
}
// FIXME: private site thumbnails?
$path = common_config('thumbnail', 'path');
if (empty($path)) {
return File::url('thumb')."/{$filename}";
}
$protocol = (GNUsocial::useHTTPS() ? 'https' : 'http');
$server = common_config('thumbnail', 'server') ?: common_config('site', 'server');
if ($path[mb_strlen($path)-1] != '/') {
$path .= '/';
}
if ($path[0] != '/') {
$path = '/'.$path;
}
return $protocol.'://'.$server.$path.$filename;
}
public function getFilename()
{
if (!File::validFilename($this->filename)) {
// TRANS: Client exception thrown if a file upload does not have a valid name.
throw new ClientException(_("Invalid filename."));
}
return $this->filename;
} }
public function getPath() public function getPath()
{ {
$filepath = self::path($this->filename); $oldpath = File::path($this->getFilename());
if (!file_exists($filepath)) { $thumbpath = self::path($this->getFilename());
throw new FileNotFoundException($filepath);
// If we have a file in our old thumbnail storage path, move it to the new one
if (file_exists($oldpath) && !file_exists($thumbpath)) {
if ($this->getFilename() === $this->getFile()->filename) {
// special case where thumbnail file exactly matches stored File
common_debug('File filename and File_thumbnail filename match on '.$this->file_id);
} elseif (!rename($oldpath, $thumbpath)) {
common_log(LOG_ERR, 'Could not move thumbnail from '._ve($oldpath).' to '._ve($thumbpath));
throw new ServerException('Could not move thumbnail from old path to new path.');
} else {
common_log(LOG_DEBUG, 'Moved thumbnail '.$this->file_id.' from '._ve($oldpath).' to '._ve($thumbpath));
}
} elseif (!file_exists($thumbpath)) {
throw new FileNotFoundException($thumbpath);
} }
return $filepath; return $thumbpath;
} }
public function getUrl() public function getUrl()
@ -188,10 +241,14 @@ class File_thumbnail extends Managed_DataObject
public function delete($useWhere=false) public function delete($useWhere=false)
{ {
if (!empty($this->filename) && file_exists(File_thumbnail::path($this->filename))) { if (!empty($this->filename)) {
$deleted = @unlink(self::path($this->filename)); try {
if (!$deleted) { $deleted = @unlink($this->getPath());
common_log(LOG_ERR, sprintf('Could not unlink existing file: "%s"', self::path($this->filename))); if (!$deleted) {
common_log(LOG_ERR, 'Could not unlink existing thumbnail file: '._ve($this->getPath()));
}
} catch (FileNotFoundException $e) {
common_log(LOG_INFO, 'Thumbnail already gone from '._ve($e->path));
} }
} }

View File

@ -204,11 +204,7 @@ class AttachmentListItem extends Widget
*/ */
protected function scrubHtmlFile(File $attachment) protected function scrubHtmlFile(File $attachment)
{ {
$path = File::path($attachment->filename); $path = $attachment->getPath();
if (!file_exists($path) || !is_readable($path)) {
common_log(LOG_ERR, "Missing local HTML attachment $path");
return false;
}
$raw = file_get_contents($path); $raw = file_get_contents($path);
// Normalize... // Normalize...

View File

@ -271,13 +271,18 @@ $default =
'exe' => false, // this would deny any uploads to keep the "exe" file extension 'exe' => false, // this would deny any uploads to keep the "exe" file extension
], ],
), ),
'thumbnail' => 'thumbnail' => [
array('crop' => false, // overridden to true if thumb height === null 'dir' => null, // falls back to File::path('thumb') (equivalent to ['attachments']['dir'] . '/thumb/')
'path' => null, // falls back to generating a URL with File::url('thumb/$filename') (equivalent to ['attachments']['path'] . '/thumb/')
'server' => null, // Only used if ['thumbnail']['path'] is NOT empty, and then it falls back to ['site']['server'], schema is decided from GNUsocial::useHTTPS()
'crop' => false, // overridden to true if thumb height === null
'maxsize' => 1000, // thumbs with an edge larger than this will not be generated 'maxsize' => 1000, // thumbs with an edge larger than this will not be generated
'width' => 450, 'width' => 450,
'height' => 600, 'height' => 600,
'upscale' => false, 'upscale' => false,
'animated' => false), // null="UseFileAsThumbnail", false="can use still frame". true requires ImageMagickPlugin 'animated' => false, // null="UseFileAsThumbnail", false="can use still frame". true requires ImageMagickPlugin
],
'application' => 'application' =>
array('desclimit' => null), array('desclimit' => null),
'group' => 'group' =>

View File

@ -429,10 +429,28 @@ class GNUsocial
*/ */
static function verifyLoadedConfig() static function verifyLoadedConfig()
{ {
$mkdirs = [];
if (common_config('htmlpurifier', 'Cache.DefinitionImpl') === 'Serializer' if (common_config('htmlpurifier', 'Cache.DefinitionImpl') === 'Serializer'
&& !is_dir(common_config('htmlpurifier', 'Cache.SerializerPath'))) { && !is_dir(common_config('htmlpurifier', 'Cache.SerializerPath'))) {
if (!mkdir(common_config('htmlpurifier', 'Cache.SerializerPath'))) { $mkdirs[common_config('htmlpurifier', 'Cache.SerializerPath')] = 'HTMLPurifier Serializer cache';
throw new ConfigException('Could not create HTMLPurifier cache dir: '._ve(common_config('htmlpurifier', 'Cache.SerializerPath'))); }
// go through our configurable storage directories
foreach (['attachments', 'thumbnail'] as $dirtype) {
$dir = common_config($dirtype, 'dir');
if (!empty($dir) && !is_dir($dir)) {
$mkdirs[$dir] = $dirtype;
}
}
// try to create those that are not directories
foreach ($mkdirs as $dir=>$description) {
if (is_file($dir)) {
throw new ConfigException('Expected directory for '._ve($description).' is a file!');
}
if (!mkdir($dir)) {
throw new ConfigException('Could not create directory for '._ve($description).': '._ve($dir));
} }
} }

View File

@ -153,8 +153,14 @@ class ImageFile
$image = new ImageFile($file->getID(), $imgPath); $image = new ImageFile($file->getID(), $imgPath);
} catch (UnsupportedMediaException $e) { } catch (UnsupportedMediaException $e) {
// Avoid deleting the original // Avoid deleting the original
if ($imgPath != $file->getPath()) { try {
unlink($imgPath); if ($imgPath !== $file->getPath()) {
@unlink($imgPath);
}
} catch (FileNotFoundException $e) {
// File reported (via getPath) that the original file
// doesn't exist anyway, so it's safe to delete $imgPath
@unlink($imgPath);
} }
throw $e; throw $e;
} }
@ -607,10 +613,15 @@ class ImageFile
// Perform resize and store into file // Perform resize and store into file
$this->resizeTo($outpath, $box); $this->resizeTo($outpath, $box);
// Avoid deleting the original try {
if ($this->getPath() != File_thumbnail::path($this->filename)) { // Avoid deleting the original
$this->unlink(); if (!in_array($this->getPath(), [File::path($this->filename), File_thumbnail::path($this->filename)])) {
$this->unlink();
}
} catch (FileNotFoundException $e) {
// $this->getPath() says the file doesn't exist anyway, so no point in trying to delete it!
} }
return File_thumbnail::saveThumbnail($this->fileRecord->getID(), return File_thumbnail::saveThumbnail($this->fileRecord->getID(),
null, // no url since we generated it ourselves and can dynamically generate the url null, // no url since we generated it ourselves and can dynamically generate the url
$width, $height, $width, $height,

View File

@ -328,7 +328,7 @@ class OembedPlugin extends Plugin
$ext = File::guessMimeExtension($info['mime']); $ext = File::guessMimeExtension($info['mime']);
// We'll trust sha256 (File::FILEHASH_ALG) not to have collision issues any time soon :) // We'll trust sha256 (File::FILEHASH_ALG) not to have collision issues any time soon :)
$filename = hash(File::FILEHASH_ALG, $imgData) . ".{$ext}"; $filename = 'oembed-'.hash(File::FILEHASH_ALG, $imgData) . ".{$ext}";
$fullpath = File_thumbnail::path($filename); $fullpath = File_thumbnail::path($filename);
// Write the file to disk. Throw Exception on failure // Write the file to disk. Throw Exception on failure
if (!file_exists($fullpath) && file_put_contents($fullpath, $imgData) === false) { if (!file_exists($fullpath) && file_put_contents($fullpath, $imgData) === false) {