2022-03-15 18:00:53 +00:00
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
namespace ASC.Web.Core.Users ;
[Transient]
public sealed class ResizeWorkerItem : DistributedTask
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
public ResizeWorkerItem ( )
2019-06-07 08:59:07 +00:00
{
2021-06-11 11:48:49 +00:00
2022-03-17 15:13:38 +00:00
}
2021-06-11 11:48:49 +00:00
2022-03-17 15:13:38 +00:00
public ResizeWorkerItem ( Guid userId , byte [ ] data , long maxFileSize , Size size , IDataStore dataStore , UserPhotoThumbnailSettings settings ) : base ( )
{
UserId = userId ;
Data = data ;
MaxFileSize = maxFileSize ;
Size = size ;
DataStore = dataStore ;
Settings = settings ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public Size Size { get ; }
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public IDataStore DataStore { get ; }
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public long MaxFileSize { get ; }
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public byte [ ] Data { get ; }
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public Guid UserId { get ; }
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public UserPhotoThumbnailSettings Settings { get ; }
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public override bool Equals ( object obj )
{
if ( obj is null )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
return false ;
}
2022-03-17 15:01:39 +00:00
2022-03-17 15:13:38 +00:00
if ( ReferenceEquals ( this , obj ) )
{
return true ;
2019-06-07 08:59:07 +00:00
}
2022-04-14 19:23:57 +00:00
if ( obj is not ResizeWorkerItem )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
return false ;
}
2022-03-17 15:01:39 +00:00
2022-03-17 15:13:38 +00:00
return Equals ( ( ResizeWorkerItem ) obj ) ;
}
2022-03-17 15:01:39 +00:00
2022-03-17 15:13:38 +00:00
public bool Equals ( ResizeWorkerItem other )
{
if ( other is null )
{
return false ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
if ( ReferenceEquals ( this , other ) )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
return true ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
return other . UserId . Equals ( UserId ) & & other . MaxFileSize = = MaxFileSize & & other . Size . Equals ( Size ) ;
}
public override int GetHashCode ( )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
return HashCode . Combine ( UserId , MaxFileSize , Size ) ;
}
}
[Singletone]
public class UserPhotoManagerCache
{
2022-03-17 16:57:02 +00:00
private readonly ConcurrentDictionary < CacheSize , ConcurrentDictionary < Guid , string > > _photofiles ;
private readonly ICacheNotify < UserPhotoManagerCacheItem > _cacheNotify ;
private readonly HashSet < int > _tenantDiskCache ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public UserPhotoManagerCache ( ICacheNotify < UserPhotoManagerCacheItem > notify )
{
try
2019-06-07 08:59:07 +00:00
{
2022-03-17 16:57:02 +00:00
_photofiles = new ConcurrentDictionary < CacheSize , ConcurrentDictionary < Guid , string > > ( ) ;
_tenantDiskCache = new HashSet < int > ( ) ;
_cacheNotify = notify ;
2022-03-17 15:13:38 +00:00
2022-03-17 16:57:02 +00:00
_cacheNotify . Subscribe ( ( data ) = >
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
var userId = new Guid ( data . UserId ) ;
2022-03-17 16:57:02 +00:00
_photofiles . GetOrAdd ( data . Size , ( r ) = > new ConcurrentDictionary < Guid , string > ( ) ) [ userId ] = data . FileName ;
2022-04-15 09:08:06 +00:00
} , CacheNotifyAction . InsertOrUpdate ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 16:57:02 +00:00
_cacheNotify . Subscribe ( ( data ) = >
2022-03-17 15:13:38 +00:00
{
var userId = new Guid ( data . UserId ) ;
2019-07-22 12:31:09 +00:00
2022-03-17 15:13:38 +00:00
try
2019-07-22 12:31:09 +00:00
{
2022-03-17 15:13:38 +00:00
foreach ( var s in ( CacheSize [ ] ) Enum . GetValues ( typeof ( CacheSize ) ) )
2019-06-07 08:59:07 +00:00
{
2022-03-17 16:57:02 +00:00
_photofiles . TryGetValue ( s , out var dict ) ;
2022-03-17 15:13:38 +00:00
dict ? . TryRemove ( userId , out _ ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
SetCacheLoadedForTenant ( false , data . TenantId ) ;
}
catch { }
2022-04-15 09:08:06 +00:00
} , CacheNotifyAction . Remove ) ;
2022-03-17 15:13:38 +00:00
}
catch ( Exception )
{
2019-08-15 12:04:42 +00:00
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
}
public bool IsCacheLoadedForTenant ( int tenantId )
{
2022-03-17 16:57:02 +00:00
return _tenantDiskCache . Contains ( tenantId ) ;
2022-03-17 15:13:38 +00:00
}
public bool SetCacheLoadedForTenant ( bool isLoaded , int tenantId )
{
2022-03-17 16:57:02 +00:00
return isLoaded ? _tenantDiskCache . Add ( tenantId ) : _tenantDiskCache . Remove ( tenantId ) ;
2022-03-17 15:13:38 +00:00
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public void ClearCache ( Guid userID , int tenantId )
{
2022-03-17 16:57:02 +00:00
if ( _cacheNotify ! = null )
2019-10-14 08:23:45 +00:00
{
2022-04-15 09:08:06 +00:00
_cacheNotify . Publish ( new UserPhotoManagerCacheItem { UserId = userID . ToString ( ) , TenantId = tenantId } , CacheNotifyAction . Remove ) ;
2019-10-14 08:23:45 +00:00
}
2022-03-17 15:13:38 +00:00
}
2019-10-14 08:23:45 +00:00
2022-03-17 15:13:38 +00:00
public void AddToCache ( Guid userID , Size size , string fileName , int tenantId )
{
2022-03-17 16:57:02 +00:00
if ( _cacheNotify ! = null )
2019-10-14 08:23:45 +00:00
{
2022-04-15 09:08:06 +00:00
_cacheNotify . Publish ( new UserPhotoManagerCacheItem { UserId = userID . ToString ( ) , Size = UserPhotoManager . ToCache ( size ) , FileName = fileName , TenantId = tenantId } , CacheNotifyAction . InsertOrUpdate ) ;
2019-10-14 08:23:45 +00:00
}
2022-03-17 15:13:38 +00:00
}
2019-10-14 08:23:45 +00:00
2022-03-17 15:13:38 +00:00
public string SearchInCache ( Guid userId , Size size )
{
string fileName = null ;
2022-03-17 16:57:02 +00:00
_photofiles . TryGetValue ( UserPhotoManager . ToCache ( size ) , out var photo ) ;
2022-03-17 15:13:38 +00:00
if ( size ! = Size . Empty )
2019-10-14 08:23:45 +00:00
{
2022-03-17 15:13:38 +00:00
photo ? . TryGetValue ( userId , out fileName ) ;
2019-10-14 08:23:45 +00:00
}
2022-03-17 15:13:38 +00:00
else
2019-10-14 08:23:45 +00:00
{
2022-03-17 15:13:38 +00:00
fileName = ( photo ? . FirstOrDefault ( x = > x . Key = = userId & & ! string . IsNullOrEmpty ( x . Value ) & & x . Value . Contains ( "_orig_" ) ) ) ? . Value ;
2019-10-14 08:23:45 +00:00
}
2022-03-17 15:13:38 +00:00
return fileName ;
}
}
2019-10-14 08:23:45 +00:00
2022-03-17 15:13:38 +00:00
[Scope(Additional = typeof(ResizeWorkerItemExtension))]
public class UserPhotoManager
{
2022-04-14 12:21:06 +00:00
public const string CUSTOM_DISTRIBUTED_TASK_QUEUE_NAME = "user_photo_manager" ;
2022-03-17 15:13:38 +00:00
//Regex for parsing filenames into groups with id's
2022-03-17 16:57:02 +00:00
private static readonly Regex _parseFile =
2022-03-17 15:13:38 +00:00
new Regex ( @"^(?'module'\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1}){0,1}" +
@"(?'user'\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1}){1}" +
@"_(?'kind'orig|size){1}_(?'size'(?'width'[0-9]{1,5})-{1}(?'height'[0-9]{1,5})){0,1}\..*" , RegexOptions . Compiled ) ;
2022-04-14 12:21:06 +00:00
private readonly UserManager _userManager ;
private readonly WebImageSupplier _webImageSupplier ;
private readonly TenantManager _tenantManager ;
private readonly StorageFactory _storageFactory ;
private readonly UserPhotoManagerCache _userPhotoManagerCache ;
private readonly SettingsManager _settingsManager ;
2022-05-26 09:01:54 +00:00
private readonly ILogger < UserPhotoManager > _log ;
2022-03-17 15:13:38 +00:00
2022-03-17 16:57:02 +00:00
private Tenant _tenant ;
2022-04-14 12:21:06 +00:00
public Tenant Tenant { get { return _tenant ? ? = _tenantManager . GetCurrentTenant ( ) ; } }
2022-03-17 15:13:38 +00:00
//note: using auto stop queue
2022-03-17 16:57:02 +00:00
private readonly DistributedTaskQueue _resizeQueue ; //TODO: configure
2022-03-17 15:13:38 +00:00
public UserPhotoManager (
UserManager userManager ,
WebImageSupplier webImageSupplier ,
TenantManager tenantManager ,
StorageFactory storageFactory ,
UserPhotoManagerCache userPhotoManagerCache ,
2022-04-26 17:53:48 +00:00
ILogger < UserPhotoManager > logger ,
2022-04-14 12:21:06 +00:00
IDistributedTaskQueueFactory queueFactory ,
SettingsManager settingsManager )
2022-03-17 15:13:38 +00:00
{
2022-04-14 12:21:06 +00:00
_resizeQueue = queueFactory . CreateQueue ( CUSTOM_DISTRIBUTED_TASK_QUEUE_NAME ) ;
_userManager = userManager ;
_webImageSupplier = webImageSupplier ;
_tenantManager = tenantManager ;
_storageFactory = storageFactory ;
_userPhotoManagerCache = userPhotoManagerCache ;
_settingsManager = settingsManager ;
_log = logger ;
2022-03-17 15:13:38 +00:00
}
2019-10-14 08:23:45 +00:00
2022-03-17 15:13:38 +00:00
private string _defaultAbsoluteWebPath ;
public string GetDefaultPhotoAbsoluteWebPath ( )
{
2022-04-14 12:21:06 +00:00
return _defaultAbsoluteWebPath ? ? = _webImageSupplier . GetAbsoluteWebPath ( _defaultAvatar ) ;
2019-10-14 08:23:45 +00:00
}
2022-03-17 15:13:38 +00:00
public string GetRetinaPhotoURL ( Guid userID )
2019-10-14 08:23:45 +00:00
{
2022-03-17 15:13:38 +00:00
return GetRetinaPhotoURL ( userID , out _ ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public string GetRetinaPhotoURL ( Guid userID , out bool isdef )
{
return GetSizedPhotoAbsoluteWebPath ( userID , RetinaFotoSize , out isdef ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public string GetMaxPhotoURL ( Guid userID )
{
return GetMaxPhotoURL ( userID , out _ ) ;
}
2019-07-24 12:34:23 +00:00
2022-03-17 15:13:38 +00:00
public string GetMaxPhotoURL ( Guid userID , out bool isdef )
{
return GetSizedPhotoAbsoluteWebPath ( userID , MaxFotoSize , out isdef ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public string GetBigPhotoURL ( Guid userID )
{
return GetBigPhotoURL ( userID , out _ ) ;
}
2019-07-24 12:34:23 +00:00
2022-03-17 15:13:38 +00:00
public string GetBigPhotoURL ( Guid userID , out bool isdef )
{
return GetSizedPhotoAbsoluteWebPath ( userID , BigFotoSize , out isdef ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public string GetMediumPhotoURL ( Guid userID )
{
return GetMediumPhotoURL ( userID , out _ ) ;
}
2019-07-24 12:34:23 +00:00
2022-03-17 15:13:38 +00:00
public string GetMediumPhotoURL ( Guid userID , out bool isdef )
{
return GetSizedPhotoAbsoluteWebPath ( userID , MediumFotoSize , out isdef ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public string GetSmallPhotoURL ( Guid userID )
{
return GetSmallPhotoURL ( userID , out _ ) ;
}
2019-07-24 12:34:23 +00:00
2022-03-17 15:13:38 +00:00
public string GetSmallPhotoURL ( Guid userID , out bool isdef )
{
return GetSizedPhotoAbsoluteWebPath ( userID , SmallFotoSize , out isdef ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public string GetSizedPhotoUrl ( Guid userId , int width , int height )
{
return GetSizedPhotoAbsoluteWebPath ( userId , new Size ( width , height ) ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
private string _defaultSmallPhotoURL ;
public string GetDefaultSmallPhotoURL ( )
{
return _defaultSmallPhotoURL ? ? = GetDefaultPhotoAbsoluteWebPath ( SmallFotoSize ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
private string _defaultMediumPhotoURL ;
public string GetDefaultMediumPhotoURL ( )
{
return _defaultMediumPhotoURL ? ? = GetDefaultPhotoAbsoluteWebPath ( MediumFotoSize ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
private string _defaultBigPhotoURL ;
public string GetDefaultBigPhotoURL ( )
{
return _defaultBigPhotoURL ? ? = GetDefaultPhotoAbsoluteWebPath ( BigFotoSize ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
private string _defaultMaxPhotoURL ;
public string GetDefaultMaxPhotoURL ( )
{
return _defaultMaxPhotoURL ? ? = GetDefaultPhotoAbsoluteWebPath ( MaxFotoSize ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
private string _defaultRetinaPhotoURL ;
public string GetDefaultRetinaPhotoURL ( )
{
return _defaultRetinaPhotoURL ? ? = GetDefaultPhotoAbsoluteWebPath ( RetinaFotoSize ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public static Size OriginalFotoSize { get ; } = new Size ( 1280 , 1280 ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public static Size RetinaFotoSize { get ; } = new Size ( 360 , 360 ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public static Size MaxFotoSize { get ; } = new Size ( 200 , 200 ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public static Size BigFotoSize { get ; } = new Size ( 82 , 82 ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public static Size MediumFotoSize { get ; } = new Size ( 48 , 48 ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public static Size SmallFotoSize { get ; } = new Size ( 32 , 32 ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
private static readonly string _defaultRetinaAvatar = "default_user_photo_size_360-360.png" ;
private static readonly string _defaultAvatar = "default_user_photo_size_200-200.png" ;
private static readonly string _defaultSmallAvatar = "default_user_photo_size_32-32.png" ;
private static readonly string _defaultMediumAvatar = "default_user_photo_size_48-48.png" ;
private static readonly string _defaultBigAvatar = "default_user_photo_size_82-82.png" ;
private static readonly string _tempDomainName = "temp" ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public bool UserHasAvatar ( Guid userID )
{
var path = GetPhotoAbsoluteWebPath ( userID ) ;
2022-04-15 09:08:06 +00:00
var fileName = Path . GetFileName ( path ) ;
2022-03-17 15:13:38 +00:00
return fileName ! = _defaultAvatar ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public string GetPhotoAbsoluteWebPath ( Guid userID )
{
var path = SearchInCache ( userID , Size . Empty , out _ ) ;
if ( ! string . IsNullOrEmpty ( path ) )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
return path ;
2019-06-07 08:59:07 +00:00
}
2019-08-15 12:04:42 +00:00
2022-03-17 15:13:38 +00:00
try
2019-06-07 08:59:07 +00:00
{
2022-04-14 12:21:06 +00:00
var data = _userManager . GetUserPhoto ( userID ) ;
2022-03-17 15:13:38 +00:00
string photoUrl ;
string fileName ;
if ( data = = null | | data . Length = = 0 )
2022-03-17 15:01:39 +00:00
{
2022-03-17 15:13:38 +00:00
photoUrl = GetDefaultPhotoAbsoluteWebPath ( ) ;
fileName = "default" ;
2022-03-17 15:01:39 +00:00
}
2022-03-17 15:13:38 +00:00
else
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
photoUrl = SaveOrUpdatePhoto ( userID , data , - 1 , new Size ( - 1 , - 1 ) , false , out fileName ) ;
}
2019-10-14 08:23:45 +00:00
2022-04-14 12:21:06 +00:00
_userPhotoManagerCache . AddToCache ( userID , Size . Empty , fileName , _tenant . Id ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
return photoUrl ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
catch
{
}
return GetDefaultPhotoAbsoluteWebPath ( ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
internal Size GetPhotoSize ( Guid userID )
{
var virtualPath = GetPhotoAbsoluteWebPath ( userID ) ;
if ( virtualPath = = null )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
return Size . Empty ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
try
{
var sizePart = virtualPath . Substring ( virtualPath . LastIndexOf ( '_' ) ) ;
sizePart = sizePart . Trim ( '_' ) ;
sizePart = sizePart . Remove ( sizePart . LastIndexOf ( '.' ) ) ;
return new Size ( int . Parse ( sizePart . Split ( '-' ) [ 0 ] ) , int . Parse ( sizePart . Split ( '-' ) [ 1 ] ) ) ;
}
catch
{
return Size . Empty ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
}
private string GetSizedPhotoAbsoluteWebPath ( Guid userID , Size size )
{
return GetSizedPhotoAbsoluteWebPath ( userID , size , out _ ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
private string GetSizedPhotoAbsoluteWebPath ( Guid userID , Size size , out bool isdef )
{
var res = SearchInCache ( userID , size , out isdef ) ;
if ( ! string . IsNullOrEmpty ( res ) )
2019-07-24 12:34:23 +00:00
{
2022-03-17 15:13:38 +00:00
return res ;
2019-07-24 12:34:23 +00:00
}
2022-03-17 15:13:38 +00:00
try
2019-06-07 08:59:07 +00:00
{
2022-04-14 12:21:06 +00:00
var data = _userManager . GetUserPhoto ( userID ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
if ( data = = null | | data . Length = = 0 )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
//empty photo. cache default
var photoUrl = GetDefaultPhotoAbsoluteWebPath ( size ) ;
2019-06-07 08:59:07 +00:00
2022-04-14 12:21:06 +00:00
_userPhotoManagerCache . AddToCache ( userID , size , "default" , _tenant . Id ) ;
2022-03-17 15:13:38 +00:00
isdef = true ;
return photoUrl ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
//Enqueue for sizing
SizePhoto ( userID , data , - 1 , size ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
catch { }
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
isdef = false ;
return GetDefaultPhotoAbsoluteWebPath ( size ) ;
}
private string GetDefaultPhotoAbsoluteWebPath ( Size size )
{
return size switch
{
2022-04-14 12:21:06 +00:00
Size ( var w , var h ) when w = = RetinaFotoSize . Width & & h = = RetinaFotoSize . Height = > _webImageSupplier . GetAbsoluteWebPath ( _defaultRetinaAvatar ) ,
Size ( var w , var h ) when w = = MaxFotoSize . Width & & h = = MaxFotoSize . Height = > _webImageSupplier . GetAbsoluteWebPath ( _defaultAvatar ) ,
Size ( var w , var h ) when w = = BigFotoSize . Width & & h = = BigFotoSize . Height = > _webImageSupplier . GetAbsoluteWebPath ( _defaultBigAvatar ) ,
Size ( var w , var h ) when w = = SmallFotoSize . Width & & h = = SmallFotoSize . Height = > _webImageSupplier . GetAbsoluteWebPath ( _defaultSmallAvatar ) ,
Size ( var w , var h ) when w = = MediumFotoSize . Width & & h = = MediumFotoSize . Height = > _webImageSupplier . GetAbsoluteWebPath ( _defaultMediumAvatar ) ,
2022-03-17 15:13:38 +00:00
_ = > GetDefaultPhotoAbsoluteWebPath ( )
} ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 16:57:02 +00:00
private static readonly HashSet < int > _tenantDiskCache = new HashSet < int > ( ) ;
private static readonly object _diskCacheLoaderLock = new object ( ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
private string SearchInCache ( Guid userId , Size size , out bool isDef )
{
2022-04-14 12:21:06 +00:00
if ( ! _userPhotoManagerCache . IsCacheLoadedForTenant ( Tenant . Id ) )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
LoadDiskCache ( ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
isDef = false ;
2019-08-05 13:29:58 +00:00
2022-04-14 12:21:06 +00:00
var fileName = _userPhotoManagerCache . SearchInCache ( userId , size ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
if ( fileName ! = null & & fileName . StartsWith ( "default" ) )
{
isDef = true ;
return GetDefaultPhotoAbsoluteWebPath ( size ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
if ( ! string . IsNullOrEmpty ( fileName ) )
{
var store = GetDataStore ( ) ;
return store . GetUriAsync ( fileName ) . Result . ToString ( ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
return null ;
}
private void LoadDiskCache ( )
{
2022-03-17 16:57:02 +00:00
lock ( _diskCacheLoaderLock )
2019-06-07 08:59:07 +00:00
{
2022-04-14 12:21:06 +00:00
if ( ! _userPhotoManagerCache . IsCacheLoadedForTenant ( Tenant . Id ) )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
try
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
var listFileNames = GetDataStore ( ) . ListFilesRelativeAsync ( "" , "" , "*.*" , false ) . ToArrayAsync ( ) . Result ;
foreach ( var fileName in listFileNames )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
//Try parse fileName
if ( fileName ! = null )
2019-06-07 08:59:07 +00:00
{
2022-03-17 16:57:02 +00:00
var match = _parseFile . Match ( fileName ) ;
2022-03-17 15:13:38 +00:00
if ( match . Success & & match . Groups [ "user" ] . Success )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
var parsedUserId = new Guid ( match . Groups [ "user" ] . Value ) ;
var size = Size . Empty ;
if ( match . Groups [ "width" ] . Success & & match . Groups [ "height" ] . Success )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
//Parse size
size = new Size ( int . Parse ( match . Groups [ "width" ] . Value ) , int . Parse ( match . Groups [ "height" ] . Value ) ) ;
2019-06-07 08:59:07 +00:00
}
2022-04-14 12:21:06 +00:00
_userPhotoManagerCache . AddToCache ( parsedUserId , size , fileName , _tenant . Id ) ;
2019-06-07 08:59:07 +00:00
}
}
}
2022-04-14 12:21:06 +00:00
_userPhotoManagerCache . SetCacheLoadedForTenant ( true , Tenant . Id ) ;
2022-03-17 15:13:38 +00:00
}
catch ( Exception err )
{
2022-05-13 14:48:49 +00:00
_log . ErrorLoadDiskCache ( err ) ;
2019-06-07 08:59:07 +00:00
}
}
}
2022-03-17 15:13:38 +00:00
}
public void ResetThumbnailSettings ( Guid userId )
{
2022-04-14 12:21:06 +00:00
var thumbSettings = _settingsManager . GetDefault < UserPhotoThumbnailSettings > ( ) ;
_settingsManager . SaveForUser ( thumbSettings , userId ) ;
2022-03-17 15:13:38 +00:00
}
public string SaveOrUpdatePhoto ( Guid userID , byte [ ] data )
{
return SaveOrUpdatePhoto ( userID , data , - 1 , OriginalFotoSize , true , out _ ) ;
}
public void RemovePhoto ( Guid idUser )
{
2022-04-14 12:21:06 +00:00
_userManager . SaveUserPhoto ( idUser , null ) ;
2022-03-17 15:13:38 +00:00
try
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
var storage = GetDataStore ( ) ;
storage . DeleteFilesAsync ( "" , idUser + "*.*" , false ) . Wait ( ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
catch ( DirectoryNotFoundException e )
2019-06-07 08:59:07 +00:00
{
2022-05-13 14:48:49 +00:00
_log . ErrorRemovePhoto ( e ) ;
2019-06-07 08:59:07 +00:00
}
2022-04-14 12:21:06 +00:00
_userManager . SaveUserPhoto ( idUser , null ) ;
_userPhotoManagerCache . ClearCache ( idUser , _tenant . Id ) ;
2022-03-17 15:13:38 +00:00
}
public void SyncPhoto ( Guid userID , byte [ ] data )
{
data = TryParseImage ( data , - 1 , OriginalFotoSize , out _ , out var width , out var height ) ;
2022-04-14 12:21:06 +00:00
_userManager . SaveUserPhoto ( userID , data ) ;
2022-03-17 15:13:38 +00:00
SetUserPhotoThumbnailSettings ( userID , width , height ) ;
2022-04-14 12:21:06 +00:00
_userPhotoManagerCache . ClearCache ( userID , _tenant . Id ) ;
2022-03-17 15:13:38 +00:00
}
2019-09-13 13:15:51 +00:00
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
private string SaveOrUpdatePhoto ( Guid userID , byte [ ] data , long maxFileSize , Size size , bool saveInCoreContext , out string fileName )
{
data = TryParseImage ( data , maxFileSize , size , out var imgFormat , out var width , out var height ) ;
var widening = CommonPhotoManager . GetImgFormatName ( imgFormat ) ;
fileName = string . Format ( "{0}_orig_{1}-{2}.{3}" , userID , width , height , widening ) ;
if ( saveInCoreContext )
2021-05-24 10:26:13 +00:00
{
2022-04-14 12:21:06 +00:00
_userManager . SaveUserPhoto ( userID , data ) ;
2021-05-24 10:26:13 +00:00
SetUserPhotoThumbnailSettings ( userID , width , height ) ;
2022-04-14 12:21:06 +00:00
_userPhotoManagerCache . ClearCache ( userID , _tenant . Id ) ;
2022-03-17 15:13:38 +00:00
2021-05-24 10:26:13 +00:00
}
2022-03-17 15:13:38 +00:00
var store = GetDataStore ( ) ;
2021-05-24 10:26:13 +00:00
2022-03-17 15:13:38 +00:00
var photoUrl = GetDefaultPhotoAbsoluteWebPath ( ) ;
if ( data ! = null & & data . Length > 0 )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
using ( var stream = new MemoryStream ( data ) )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
photoUrl = store . SaveAsync ( fileName , stream ) . Result . ToString ( ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
//Queue resizing
SizePhoto ( userID , data , - 1 , SmallFotoSize , true ) ;
SizePhoto ( userID , data , - 1 , MediumFotoSize , true ) ;
SizePhoto ( userID , data , - 1 , BigFotoSize , true ) ;
SizePhoto ( userID , data , - 1 , MaxFotoSize , true ) ;
SizePhoto ( userID , data , - 1 , RetinaFotoSize , true ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
return photoUrl ;
}
private void SetUserPhotoThumbnailSettings ( Guid userId , int width , int height )
{
2022-04-14 12:21:06 +00:00
var settings = _settingsManager . LoadForUser < UserPhotoThumbnailSettings > ( userId ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
if ( ! settings . IsDefault )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
return ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
var max = Math . Max ( Math . Max ( width , height ) , SmallFotoSize . Width ) ;
var min = Math . Max ( Math . Min ( width , height ) , SmallFotoSize . Width ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
var pos = ( max - min ) / 2 ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
settings = new UserPhotoThumbnailSettings (
width > = height ? new Point ( pos , 0 ) : new Point ( 0 , pos ) ,
new Size ( min , min ) ) ;
2019-06-07 08:59:07 +00:00
2022-04-14 12:21:06 +00:00
_settingsManager . SaveForUser ( settings , userId ) ;
2022-03-17 15:13:38 +00:00
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
private byte [ ] TryParseImage ( byte [ ] data , long maxFileSize , Size maxsize , out IImageFormat imgFormat , out int width , out int height )
{
if ( data = = null | | data . Length < = 0 )
{
throw new UnknownImageFormatException ( ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
if ( maxFileSize ! = - 1 & & data . Length > maxFileSize )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
throw new ImageSizeLimitException ( ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
//data = ImageHelper.RotateImageByExifOrientationData(data, Log);
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
try
{
using var img = Image . Load ( data , out var format ) ;
imgFormat = format ;
width = img . Width ;
height = img . Height ;
var maxWidth = maxsize . Width ;
var maxHeight = maxsize . Height ;
if ( ( maxHeight ! = - 1 & & img . Height > maxHeight ) | | ( maxWidth ! = - 1 & & img . Width > maxWidth ) )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
#region calulate height and width
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
if ( width > maxWidth & & height > maxHeight )
{
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
if ( width > height )
2019-08-15 15:08:40 +00:00
{
2020-11-17 10:00:01 +00:00
height = ( int ) ( height * ( double ) maxWidth / width + 0.5 ) ;
2019-08-15 15:08:40 +00:00
width = maxWidth ;
}
2022-03-17 15:13:38 +00:00
else
2019-08-15 15:08:40 +00:00
{
2020-11-17 10:00:01 +00:00
width = ( int ) ( width * ( double ) maxHeight / height + 0.5 ) ;
2019-08-15 15:08:40 +00:00
height = maxHeight ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
}
2019-08-15 15:08:40 +00:00
2022-03-17 15:13:38 +00:00
if ( width > maxWidth & & height < = maxHeight )
{
height = ( int ) ( height * ( double ) maxWidth / width + 0.5 ) ;
width = maxWidth ;
}
2019-08-15 15:08:40 +00:00
2022-03-17 15:13:38 +00:00
if ( width < = maxWidth & & height > maxHeight )
{
width = ( int ) ( width * ( double ) maxHeight / height + 0.5 ) ;
height = maxHeight ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
var tmpW = width ;
var tmpH = height ;
#endregion
using var destRound = img . Clone ( x = > x . Resize ( new ResizeOptions
{
Size = new Size ( tmpW , tmpH ) ,
Mode = ResizeMode . Stretch
} ) ) ;
data = CommonPhotoManager . SaveToBytes ( destRound ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
return data ;
}
catch ( OutOfMemoryException )
{
throw new ImageSizeLimitException ( ) ;
}
catch ( ArgumentException error )
{
throw new UnknownImageFormatException ( error ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
}
private string SizePhoto ( Guid userID , byte [ ] data , long maxFileSize , Size size )
{
return SizePhoto ( userID , data , maxFileSize , size , false ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
private string SizePhoto ( Guid userID , byte [ ] data , long maxFileSize , Size size , bool now )
{
if ( data = = null | | data . Length < = 0 )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
throw new UnknownImageFormatException ( ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
if ( maxFileSize ! = - 1 & & data . Length > maxFileSize )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
throw new ImageWeightLimitException ( ) ;
}
2022-03-17 15:01:39 +00:00
2022-04-14 12:21:06 +00:00
var resizeTask = new ResizeWorkerItem ( userID , data , maxFileSize , size , GetDataStore ( ) , _settingsManager . LoadForUser < UserPhotoThumbnailSettings > ( userID ) ) ;
2022-03-17 15:13:38 +00:00
var key = $"{userID}{size}" ;
2022-04-14 12:21:06 +00:00
resizeTask [ "key" ] = key ;
2022-03-17 15:13:38 +00:00
if ( now )
{
//Resize synchronously
ResizeImage ( resizeTask ) ;
return GetSizedPhotoAbsoluteWebPath ( userID , size ) ;
}
else
{
2022-04-14 12:21:06 +00:00
if ( ! _resizeQueue . GetAllTasks < ResizeWorkerItem > ( ) . Any ( r = > r [ "key" ] = = key ) )
2022-03-17 15:01:39 +00:00
{
2022-03-17 15:13:38 +00:00
//Add
2022-04-14 12:21:06 +00:00
_resizeQueue . EnqueueTask ( ( a , b ) = > ResizeImage ( resizeTask ) , resizeTask ) ;
2022-03-17 15:01:39 +00:00
}
2022-03-17 15:13:38 +00:00
return GetDefaultPhotoAbsoluteWebPath ( size ) ;
//NOTE: return default photo here. Since task will update cache
}
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
private void ResizeImage ( ResizeWorkerItem item )
{
try
{
var data = item . Data ;
using var stream = new MemoryStream ( data ) ;
using var img = Image . Load ( stream , out var format ) ;
var imgFormat = format ;
if ( item . Size ! = img . Size ( ) )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
using var img2 = item . Settings . IsDefault ?
CommonPhotoManager . DoThumbnail ( img , item . Size , true , true , true ) :
UserPhotoThumbnailManager . GetImage ( img , item . Size , item . Settings ) ;
data = CommonPhotoManager . SaveToBytes ( img2 ) ;
2019-06-07 08:59:07 +00:00
}
else
{
2022-03-17 15:13:38 +00:00
data = CommonPhotoManager . SaveToBytes ( img ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
var widening = CommonPhotoManager . GetImgFormatName ( imgFormat ) ;
var fileName = string . Format ( "{0}_size_{1}-{2}.{3}" , item . UserId , item . Size . Width , item . Size . Height , widening ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
using var stream2 = new MemoryStream ( data ) ;
item . DataStore . SaveAsync ( fileName , stream2 ) . Result . ToString ( ) ;
2019-06-07 08:59:07 +00:00
2022-04-14 12:21:06 +00:00
_userPhotoManagerCache . AddToCache ( item . UserId , item . Size , fileName , _tenant . Id ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
catch ( ArgumentException error )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
throw new UnknownImageFormatException ( error ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public string GetTempPhotoAbsoluteWebPath ( string fileName )
{
return GetDataStore ( ) . GetUriAsync ( _tempDomainName , fileName ) . Result . ToString ( ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public string SaveTempPhoto ( byte [ ] data , long maxFileSize , int maxWidth , int maxHeight )
{
data = TryParseImage ( data , maxFileSize , new Size ( maxWidth , maxHeight ) , out var imgFormat , out _ , out _ ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
var fileName = Guid . NewGuid ( ) + "." + CommonPhotoManager . GetImgFormatName ( imgFormat ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
var store = GetDataStore ( ) ;
using var stream = new MemoryStream ( data ) ;
return store . SaveAsync ( _tempDomainName , fileName , stream ) . Result . ToString ( ) ;
}
public byte [ ] GetTempPhotoData ( string fileName )
{
using var s = GetDataStore ( ) . GetReadStreamAsync ( _tempDomainName , fileName ) . Result ;
var data = new MemoryStream ( ) ;
var buffer = new byte [ 1024 * 10 ] ;
while ( true )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
var count = s . Read ( buffer , 0 , buffer . Length ) ;
if ( count = = 0 )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
break ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
data . Write ( buffer , 0 , count ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
return data . ToArray ( ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public string GetSizedTempPhotoAbsoluteWebPath ( string fileName , int newWidth , int newHeight )
{
var store = GetDataStore ( ) ;
if ( store . IsFileAsync ( _tempDomainName , fileName ) . Result )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
using var s = store . GetReadStreamAsync ( _tempDomainName , fileName ) . Result ;
using var img = Image . Load ( s , out var format ) ;
var imgFormat = format ;
byte [ ] data ;
2019-08-15 15:08:40 +00:00
2022-03-17 15:13:38 +00:00
if ( img . Width ! = newWidth | | img . Height ! = newHeight )
{
using var img2 = CommonPhotoManager . DoThumbnail ( img , new Size ( newWidth , newHeight ) , true , true , true ) ;
data = CommonPhotoManager . SaveToBytes ( img2 ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
else
{
data = CommonPhotoManager . SaveToBytes ( img ) ;
}
var widening = CommonPhotoManager . GetImgFormatName ( imgFormat ) ;
var index = fileName . LastIndexOf ( '.' ) ;
var fileNameWithoutExt = ( index ! = - 1 ) ? fileName . Substring ( 0 , index ) : fileName ;
var trueFileName = fileNameWithoutExt + "_size_" + newWidth . ToString ( ) + "-" + newHeight . ToString ( ) + "." + widening ;
using var stream = new MemoryStream ( data ) ;
return store . SaveAsync ( _tempDomainName , trueFileName , stream ) . Result . ToString ( ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
return GetDefaultPhotoAbsoluteWebPath ( new Size ( newWidth , newHeight ) ) ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public void RemoveTempPhoto ( string fileName )
{
var index = fileName . LastIndexOf ( '.' ) ;
var fileNameWithoutExt = ( index ! = - 1 ) ? fileName . Substring ( 0 , index ) : fileName ;
try
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
var store = GetDataStore ( ) ;
store . DeleteFilesAsync ( _tempDomainName , "" , fileNameWithoutExt + "*.*" , false ) . Wait ( ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
catch { }
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public Image GetPhotoImage ( Guid userID , out IImageFormat format )
{
try
2019-06-07 08:59:07 +00:00
{
2022-04-14 12:21:06 +00:00
var data = _userManager . GetUserPhoto ( userID ) ;
2022-03-17 15:13:38 +00:00
if ( data ! = null )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
var img = Image . Load ( data , out var imgFormat ) ;
format = imgFormat ;
return img ;
2019-06-07 08:59:07 +00:00
}
}
2022-03-17 15:13:38 +00:00
catch { }
format = null ;
return null ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public string SaveThumbnail ( Guid userID , Image img , IImageFormat format )
{
var moduleID = Guid . Empty ;
var widening = CommonPhotoManager . GetImgFormatName ( format ) ;
var size = img . Size ( ) ;
var fileName = string . Format ( "{0}{1}_size_{2}-{3}.{4}" , moduleID = = Guid . Empty ? "" : moduleID . ToString ( ) , userID , img . Width , img . Height , widening ) ;
var store = GetDataStore ( ) ;
string photoUrl ;
using ( var s = new MemoryStream ( CommonPhotoManager . SaveToBytes ( img ) ) )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
img . Dispose ( ) ;
photoUrl = store . SaveAsync ( fileName , s ) . Result . ToString ( ) ;
2019-06-07 08:59:07 +00:00
}
2022-04-14 12:21:06 +00:00
_userPhotoManagerCache . AddToCache ( userID , size , fileName , _tenant . Id ) ;
2022-03-17 15:13:38 +00:00
return photoUrl ;
}
public byte [ ] GetUserPhotoData ( Guid userId , Size size )
{
try
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
var pattern = string . Format ( "{0}_size_{1}-{2}.*" , userId , size . Width , size . Height ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
var fileName = GetDataStore ( ) . ListFilesRelativeAsync ( "" , "" , pattern , false ) . ToArrayAsync ( ) . Result . FirstOrDefault ( ) ;
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
if ( string . IsNullOrEmpty ( fileName ) )
{
return null ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
using var s = GetDataStore ( ) . GetReadStreamAsync ( "" , fileName ) . Result ;
var data = new MemoryStream ( ) ;
var buffer = new byte [ 1024 * 10 ] ;
while ( true )
{
var count = s . Read ( buffer , 0 , buffer . Length ) ;
if ( count = = 0 )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
break ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
data . Write ( buffer , 0 , count ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
return data . ToArray ( ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
catch ( Exception err )
2019-06-07 08:59:07 +00:00
{
2022-05-13 14:48:49 +00:00
_log . ErrorGetUserPhotoData ( err ) ;
2022-03-17 15:13:38 +00:00
return null ;
2019-06-07 08:59:07 +00:00
}
}
2022-03-17 16:57:02 +00:00
private IDataStore _dataStore ;
2022-03-17 15:13:38 +00:00
private IDataStore GetDataStore ( )
2019-06-07 08:59:07 +00:00
{
2022-04-14 12:21:06 +00:00
return _dataStore ? ? = _storageFactory . GetStorage ( Tenant . Id . ToString ( ) , "userPhotos" ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
public static CacheSize ToCache ( Size size )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
return size switch
{
Size ( var w , var h ) when w = = RetinaFotoSize . Width & & h = = RetinaFotoSize . Height = > CacheSize . Retina ,
Size ( var w , var h ) when w = = MaxFotoSize . Width & & h = = MaxFotoSize . Height = > CacheSize . Max ,
Size ( var w , var h ) when w = = BigFotoSize . Width & & h = = BigFotoSize . Height = > CacheSize . Big ,
Size ( var w , var h ) when w = = SmallFotoSize . Width & & h = = SmallFotoSize . Height = > CacheSize . Small ,
Size ( var w , var h ) when w = = MediumFotoSize . Width & & h = = MediumFotoSize . Height = > CacheSize . Medium ,
_ = > CacheSize . Original
} ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
#region Exception Classes
public class UnknownImageFormatException : Exception
{
public UnknownImageFormatException ( ) : base ( "unknown image file type" ) { }
public UnknownImageFormatException ( Exception inner ) : base ( "unknown image file type" , inner ) { }
}
public class ImageWeightLimitException : Exception
{
public ImageWeightLimitException ( ) : base ( "image width is too large" ) { }
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
public class ImageSizeLimitException : Exception
{
public ImageSizeLimitException ( ) : base ( "image size is too large" ) { }
}
#endregion
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
/// <summary>
/// Helper class for manipulating images.
/// </summary>
/ * public static class ImageHelper
{
2019-06-07 08:59:07 +00:00
/// <summary>
2022-03-17 15:13:38 +00:00
/// Rotate the given image byte array according to Exif Orientation data
2019-06-07 08:59:07 +00:00
/// </summary>
2022-03-17 15:13:38 +00:00
/// <param name="data">source image byte array</param>
/// <param name="updateExifData">set it to TRUE to update image Exif data after rotation (default is TRUE)</param>
/// <returns>The rotated image byte array. If no rotation occurred, source data will be returned.</returns>
public static byte [ ] RotateImageByExifOrientationData ( byte [ ] data , ILog Log , bool updateExifData = true )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
try
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
using var stream = new MemoryStream ( data ) ;
using var img = Image . Load ( stream ) ;
2019-08-15 12:04:42 +00:00
2022-03-17 15:13:38 +00:00
var fType = RotateImageByExifOrientationData ( img , updateExifData ) ;
2019-06-07 08:59:07 +00:00
if ( fType ! = RotateFlipType . RotateNoneFlipNone )
{
2022-03-17 15:13:38 +00:00
using var tempStream = new MemoryStream ( ) ;
img . Save ( tempStream , System . Drawing . Imaging . ImageFormat . Png ) ;
data = tempStream . ToArray ( ) ;
2019-06-07 08:59:07 +00:00
}
}
2022-03-17 15:13:38 +00:00
catch ( Exception err )
{
Log . Error ( err ) ;
}
return data ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
/// <summary>
/// Rotate the given image file according to Exif Orientation data
/// </summary>
/// <param name="sourceFilePath">path of source file</param>
/// <param name="targetFilePath">path of target file</param>
/// <param name="targetFormat">target format</param>
/// <param name="updateExifData">set it to TRUE to update image Exif data after rotation (default is TRUE)</param>
/// <returns>The RotateFlipType value corresponding to the applied rotation. If no rotation occurred, RotateFlipType.RotateNoneFlipNone will be returned.</returns>
public static RotateFlipType RotateImageByExifOrientationData ( string sourceFilePath , string targetFilePath , ImageFormat targetFormat , bool updateExifData = true )
{
// Rotate the image according to EXIF data
using var bmp = new Bitmap ( sourceFilePath ) ;
var fType = RotateImageByExifOrientationData ( bmp , updateExifData ) ;
if ( fType ! = RotateFlipType . RotateNoneFlipNone )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
bmp . Save ( targetFilePath , targetFormat ) ;
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
return fType ;
}
2019-06-07 08:59:07 +00:00
2022-03-17 15:13:38 +00:00
/// <summary>
/// Rotate the given bitmap according to Exif Orientation data
/// </summary>
/// <param name="img">source image</param>
/// <param name="updateExifData">set it to TRUE to update image Exif data after rotation (default is TRUE)</param>
/// <returns>The RotateFlipType value corresponding to the applied rotation. If no rotation occurred, RotateFlipType.RotateNoneFlipNone will be returned.</returns>
public static RotateFlipType RotateImageByExifOrientationData ( Image img , bool updateExifData = true )
{
const int orientationId = 0x0112 ;
var fType = RotateFlipType . RotateNoneFlipNone ;
if ( img . PropertyIdList . Contains ( orientationId ) )
2019-06-07 08:59:07 +00:00
{
2022-03-17 15:13:38 +00:00
var pItem = img . GetPropertyItem ( orientationId ) ;
fType = GetRotateFlipTypeByExifOrientationData ( pItem . Value [ 0 ] ) ;
if ( fType ! = RotateFlipType . RotateNoneFlipNone )
2019-08-15 15:13:25 +00:00
{
2022-03-17 15:13:38 +00:00
img . RotateFlip ( fType ) ;
if ( updateExifData ) img . RemovePropertyItem ( orientationId ) ; // Remove Exif orientation tag
}
2019-06-07 08:59:07 +00:00
}
2022-03-17 15:13:38 +00:00
return fType ;
}
2019-08-27 14:57:06 +00:00
2022-03-17 15:13:38 +00:00
/// <summary>
/// Return the proper System.Drawing.RotateFlipType according to given orientation EXIF metadata
/// </summary>
/// <param name="orientation">Exif "Orientation"</param>
/// <returns>the corresponding System.Drawing.RotateFlipType enum value</returns>
public static RotateFlipType GetRotateFlipTypeByExifOrientationData ( int orientation )
2019-08-27 14:57:06 +00:00
{
2022-03-17 15:13:38 +00:00
return orientation switch
2020-09-30 14:47:42 +00:00
{
2022-03-17 15:13:38 +00:00
1 = > RotateFlipType . RotateNoneFlipNone ,
2 = > RotateFlipType . RotateNoneFlipX ,
3 = > RotateFlipType . Rotate180FlipNone ,
4 = > RotateFlipType . Rotate180FlipX ,
5 = > RotateFlipType . Rotate90FlipX ,
6 = > RotateFlipType . Rotate90FlipNone ,
7 = > RotateFlipType . Rotate270FlipX ,
8 = > RotateFlipType . Rotate270FlipNone ,
_ = > RotateFlipType . RotateNoneFlipNone ,
} ;
2019-08-27 14:57:06 +00:00
}
2022-03-17 15:13:38 +00:00
} * /
public static class SizeExtend
{
public static void Deconstruct ( this Size size , out int w , out int h )
{
( w , h ) = ( size . Width , size . Height ) ;
2019-08-27 14:57:06 +00:00
}
2022-03-17 15:13:38 +00:00
}
2019-10-31 11:28:30 +00:00
2022-03-17 15:13:38 +00:00
public static class ResizeWorkerItemExtension
{
public static void Register ( DIHelper services )
2019-10-31 11:28:30 +00:00
{
2022-04-14 12:21:06 +00:00
services . Configure < DistributedTaskQueueFactoryOptions > ( UserPhotoManager . CUSTOM_DISTRIBUTED_TASK_QUEUE_NAME , options = >
2019-10-31 11:28:30 +00:00
{
2022-04-14 12:21:06 +00:00
options . MaxThreadsCount = 2 ;
} ) ;
2019-10-31 11:28:30 +00:00
}
2022-03-17 15:13:38 +00:00
}