Merge pull request #21240 from owncloud/avatar_speedup

Avatar speedup
This commit is contained in:
Thomas Müller 2015-12-17 14:43:21 +01:00
commit e3ed42135d
5 changed files with 116 additions and 45 deletions

View File

@ -30,6 +30,7 @@ use OCP\AppFramework\Controller;
use OCP\AppFramework\Http; use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\DataDisplayResponse; use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\Files\NotFoundException;
use OCP\IAvatarManager; use OCP\IAvatarManager;
use OCP\ILogger; use OCP\ILogger;
use OCP\IL10N; use OCP\IL10N;
@ -112,20 +113,23 @@ class AvatarController extends Controller {
$size = 64; $size = 64;
} }
$avatar = $this->avatarManager->getAvatar($userId); try {
$image = $avatar->get($size); $avatar = $this->avatarManager->getAvatar($userId)->getFile($size);
$resp = new DataDisplayResponse($avatar->getContent(),
if ($image instanceof \OCP\IImage) {
$resp = new DataDisplayResponse($image->data(),
Http::STATUS_OK, Http::STATUS_OK,
['Content-Type' => $image->mimeType()]); ['Content-Type' => $avatar->getMimeType()]);
$resp->setETag(crc32($image->data())); $resp->setETag($avatar->getEtag());
} else { } catch (NotFoundException $e) {
$user = $this->userManager->get($userId); $user = $this->userManager->get($userId);
$userName = $user ? $user->getDisplayName() : '';
$resp = new DataResponse([ $resp = new DataResponse([
'data' => [ 'data' => [
'displayname' => $userName, 'displayname' => $user->getDisplayName(),
],
]);
} catch (\Exception $e) {
$resp = new DataResponse([
'data' => [
'displayname' => '',
], ],
]); ]);
} }

View File

@ -31,6 +31,7 @@ namespace OC;
use OCP\Files\Folder; use OCP\Files\Folder;
use OCP\Files\File; use OCP\Files\File;
use OCP\Files\NotFoundException;
use OCP\IL10N; use OCP\IL10N;
use OC_Image; use OC_Image;
@ -62,28 +63,14 @@ class Avatar implements \OCP\IAvatar {
* @return boolean|\OCP\IImage containing the avatar or false if there's no image * @return boolean|\OCP\IImage containing the avatar or false if there's no image
*/ */
public function get ($size = 64) { public function get ($size = 64) {
if ($this->folder->nodeExists('avatar.jpg')) { try {
$ext = 'jpg'; $file = $this->getFile($size);
} elseif ($this->folder->nodeExists('avatar.png')) { } catch (NotFoundException $e) {
$ext = 'png';
} else {
return false; return false;
} }
$avatar = new OC_Image(); $avatar = new OC_Image();
if ($this->folder->nodeExists('avatar.' . $size . '.' . $ext)) { $avatar->loadFromData($file->getContent());
/** @var File $node */
$node = $this->folder->get('avatar.' . $size . '.' . $ext);
$avatar->loadFromData($node->getContent());
} else {
/** @var File $node */
$node = $this->folder->get('avatar.' . $ext);
$avatar->loadFromData($node->getContent());
if ($size > 0) {
$avatar->resize($size);
}
$this->folder->newFile('avatar.' . $size . '.' . $ext)->putContent($avatar->data());
}
return $avatar; return $avatar;
} }
@ -144,4 +131,50 @@ class Avatar implements \OCP\IAvatar {
$this->folder->get('avatar.png')->delete(); $this->folder->get('avatar.png')->delete();
} catch (\OCP\Files\NotFoundException $e) {} } catch (\OCP\Files\NotFoundException $e) {}
} }
/**
* Get the File of an avatar of size $size.
*
* @param int $size
* @return File
* @throws NotFoundException
*/
public function getFile($size) {
$ext = $this->getExtention();
$path = 'avatar.' . $size . '.' . $ext;
try {
$file = $this->folder->get($path);
} catch (NotFoundException $e) {
if ($size <= 0) {
throw new NotFoundException;
}
$avatar = new OC_Image();
/** @var File $file */
$file = $this->folder->get('avatar.' . $ext);
$avatar->loadFromData($file->getContent());
$avatar->resize($size);
$file = $this->folder->newFile($path);
$file->putContent($avatar->data());
}
return $file;
}
/**
* Get the extention of the avatar. If there is no avatar throw Exception
*
* @return string
* @throws NotFoundException
*/
private function getExtention() {
if ($this->folder->nodeExists('avatar.jpg')) {
return 'jpg';
} elseif ($this->folder->nodeExists('avatar.png')) {
return 'png';
}
throw new NotFoundException;
}
} }

View File

@ -24,6 +24,8 @@
*/ */
namespace OCP; namespace OCP;
use OCP\Files\File;
use OCP\Files\NotFoundException;
/** /**
* This class provides avatar functionality * This class provides avatar functionality
@ -64,4 +66,13 @@ interface IAvatar {
* @since 6.0.0 * @since 6.0.0
*/ */
public function remove(); public function remove();
/**
* Get the file of the avatar
* @param int $size
* @return File
* @throws NotFoundException
* @since 9.0.0
*/
public function getFile($size);
} }

View File

@ -26,8 +26,10 @@ use OCP\AppFramework\IAppContainer;
use OCP\AppFramework\Http; use OCP\AppFramework\Http;
use OCP\Files\Folder; use OCP\Files\Folder;
use OCP\Files\File; use OCP\Files\File;
use OCP\Files\NotFoundException;
use OCP\IUser; use OCP\IUser;
use OCP\IAvatar; use OCP\IAvatar;
use Punic\Exception;
use Test\Traits\UserTrait; use Test\Traits\UserTrait;
/** /**
@ -56,6 +58,8 @@ class AvatarControllerTest extends \Test\TestCase {
private $avatarMock; private $avatarMock;
/** @var IUser */ /** @var IUser */
private $userMock; private $userMock;
/** @var File */
private $avatarFile;
protected function setUp() { protected function setUp() {
parent::setUp(); parent::setUp();
@ -88,6 +92,10 @@ class AvatarControllerTest extends \Test\TestCase {
->willReturnMap([['userId', $this->userMock]]); ->willReturnMap([['userId', $this->userMock]]);
$this->container['UserSession']->method('getUser')->willReturn($this->userMock); $this->container['UserSession']->method('getUser')->willReturn($this->userMock);
$this->avatarFile = $this->getMock('OCP\Files\File');
$this->avatarFile->method('getContnet')->willReturn('image data');
$this->avatarFile->method('getMimeType')->willReturn('image type');
$this->avatarFile->method('getEtag')->willReturn('my etag');
} }
public function tearDown() { public function tearDown() {
@ -100,6 +108,7 @@ class AvatarControllerTest extends \Test\TestCase {
*/ */
public function testGetAvatarNoAvatar() { public function testGetAvatarNoAvatar() {
$this->container['AvatarManager']->method('getAvatar')->willReturn($this->avatarMock); $this->container['AvatarManager']->method('getAvatar')->willReturn($this->avatarMock);
$this->avatarMock->method('getFile')->will($this->throwException(new NotFoundException()));
$response = $this->avatarController->getAvatar('userId', 32); $response = $this->avatarController->getAvatar('userId', 32);
//Comment out until JS is fixed //Comment out until JS is fixed
@ -112,12 +121,8 @@ class AvatarControllerTest extends \Test\TestCase {
* Fetch the user's avatar * Fetch the user's avatar
*/ */
public function testGetAvatar() { public function testGetAvatar() {
$image = $this->getMock('OCP\IImage'); $this->avatarMock->method('getFile')->willReturn($this->avatarFile);
$image->method('data')->willReturn('image data'); $this->container['AvatarManager']->method('getAvatar')->with('userId')->willReturn($this->avatarMock);
$image->method('mimeType')->willReturn('image type');
$this->avatarMock->method('get')->willReturn($image);
$this->container['AvatarManager']->method('getAvatar')->willReturn($this->avatarMock);
$response = $this->avatarController->getAvatar('userId', 32); $response = $this->avatarController->getAvatar('userId', 32);
@ -125,17 +130,19 @@ class AvatarControllerTest extends \Test\TestCase {
$this->assertArrayHasKey('Content-Type', $response->getHeaders()); $this->assertArrayHasKey('Content-Type', $response->getHeaders());
$this->assertEquals('image type', $response->getHeaders()['Content-Type']); $this->assertEquals('image type', $response->getHeaders()['Content-Type']);
$this->assertEquals(crc32('image data'), $response->getEtag()); $this->assertEquals('my etag', $response->getEtag());
} }
/** /**
* Fetch the avatar of a non-existing user * Fetch the avatar of a non-existing user
*/ */
public function testGetAvatarNoUser() { public function testGetAvatarNoUser() {
$this->avatarMock->method('get')->willReturn(null); $this->container['AvatarManager']
$this->container['AvatarManager']->method('getAvatar')->willReturn($this->avatarMock); ->method('getAvatar')
->with('userDoesNotExist')
->will($this->throwException(new \Exception('user does not exist')));
$response = $this->avatarController->getAvatar('userDoesnotexist', 32); $response = $this->avatarController->getAvatar('userDoesNotExist', 32);
//Comment out until JS is fixed //Comment out until JS is fixed
//$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus()); //$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
@ -148,8 +155,9 @@ class AvatarControllerTest extends \Test\TestCase {
*/ */
public function testGetAvatarSize() { public function testGetAvatarSize() {
$this->avatarMock->expects($this->once()) $this->avatarMock->expects($this->once())
->method('get') ->method('getFile')
->with($this->equalTo(32)); ->with($this->equalTo(32))
->willReturn($this->avatarFile);
$this->container['AvatarManager']->method('getAvatar')->willReturn($this->avatarMock); $this->container['AvatarManager']->method('getAvatar')->willReturn($this->avatarMock);
@ -161,8 +169,9 @@ class AvatarControllerTest extends \Test\TestCase {
*/ */
public function testGetAvatarSizeMin() { public function testGetAvatarSizeMin() {
$this->avatarMock->expects($this->once()) $this->avatarMock->expects($this->once())
->method('get') ->method('getFile')
->with($this->equalTo(64)); ->with($this->equalTo(64))
->willReturn($this->avatarFile);
$this->container['AvatarManager']->method('getAvatar')->willReturn($this->avatarMock); $this->container['AvatarManager']->method('getAvatar')->willReturn($this->avatarMock);
@ -174,8 +183,9 @@ class AvatarControllerTest extends \Test\TestCase {
*/ */
public function testGetAvatarSizeMax() { public function testGetAvatarSizeMax() {
$this->avatarMock->expects($this->once()) $this->avatarMock->expects($this->once())
->method('get') ->method('getFile')
->with($this->equalTo(2048)); ->with($this->equalTo(2048))
->willReturn($this->avatarFile);
$this->container['AvatarManager']->method('getAvatar')->willReturn($this->avatarMock); $this->container['AvatarManager']->method('getAvatar')->willReturn($this->avatarMock);

View File

@ -60,12 +60,25 @@ class AvatarTest extends \Test\TestCase {
$file = $this->getMock('\OCP\Files\File'); $file = $this->getMock('\OCP\Files\File');
$file->method('getContent')->willReturn($expected->data()); $file->method('getContent')->willReturn($expected->data());
$this->folder->method('get')->with('avatar.png')->willReturn($file);
$this->folder->method('get')
->will($this->returnCallback(
function($path) use ($file) {
if ($path === 'avatar.png') {
return $file;
} else {
throw new \OCP\Files\NotFoundException;
}
}
));
$newFile = $this->getMock('\OCP\Files\File'); $newFile = $this->getMock('\OCP\Files\File');
$newFile->expects($this->once()) $newFile->expects($this->once())
->method('putContent') ->method('putContent')
->with($expected2->data()); ->with($expected2->data());
$newFile->expects($this->once())
->method('getContent')
->willReturn($expected2->data());
$this->folder->expects($this->once()) $this->folder->expects($this->once())
->method('newFile') ->method('newFile')
->with('avatar.32.png') ->with('avatar.32.png')