fileconverter: moved timer to separete worker service
This commit is contained in:
parent
3757174d08
commit
7b1ea58cdf
42
common/ASC.Common/Data/TypeExtension.cs
Normal file
42
common/ASC.Common/Data/TypeExtension.cs
Normal file
@ -0,0 +1,42 @@
|
||||
// (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
|
||||
|
||||
namespace ASC.Common.Data;
|
||||
public static class TypeExtension
|
||||
{
|
||||
public static string GetFormattedName(this Type type)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var genericArguments = type.GetGenericArguments()
|
||||
.Select(x => x.Name)
|
||||
.Aggregate((x1, x2) => $"{x1}, {x2}");
|
||||
return $"{type.Name.Substring(0, type.Name.IndexOf("`"))}"
|
||||
+ $"<{genericArguments}>";
|
||||
}
|
||||
return type.Name;
|
||||
}
|
||||
}
|
@ -305,6 +305,13 @@ public class DistributedTaskQueue
|
||||
|
||||
private void SaveToCache(IEnumerable<DistributedTask> queueTasks)
|
||||
{
|
||||
if (!queueTasks.Any())
|
||||
{
|
||||
_distributedCache.Remove(_name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
|
||||
Serializer.Serialize(ms, queueTasks);
|
||||
@ -332,21 +339,11 @@ public class DistributedTaskQueue
|
||||
|
||||
private IEnumerable<DistributedTask> DeleteOrphanCacheItem(IEnumerable<DistributedTask> queueTasks)
|
||||
{
|
||||
if (!queueTasks.Any())
|
||||
{
|
||||
return queueTasks;
|
||||
}
|
||||
var listTasks = queueTasks.ToList();
|
||||
|
||||
var orphans = queueTasks.Where(IsOrphanCacheItem);
|
||||
listTasks.RemoveAll(IsOrphanCacheItem);
|
||||
|
||||
if (!orphans.Any())
|
||||
{
|
||||
return queueTasks;
|
||||
}
|
||||
|
||||
queueTasks = queueTasks.Except(queueTasks);
|
||||
|
||||
SaveToCache(queueTasks);
|
||||
SaveToCache(listTasks);
|
||||
|
||||
return queueTasks;
|
||||
}
|
||||
|
@ -149,3 +149,4 @@ global using ProtoBuf;
|
||||
global using Telegram.Bot;
|
||||
|
||||
global using static ASC.Security.Cryptography.EmailValidationKeyProvider;
|
||||
global using ASC.Common.Data;
|
@ -79,7 +79,7 @@ public class RegisterInstanceDao<T> : IRegisterInstanceDao<T> where T : IHostedS
|
||||
public async Task<IEnumerable<InstanceRegistration>> GetAll()
|
||||
{
|
||||
return await _instanceRegistrationContext.InstanceRegistrations
|
||||
.Where(x => x.WorkerTypeName == typeof(T).Name)
|
||||
.Where(x => x.WorkerTypeName == typeof(T).GetFormattedName())
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class RegisterInstanceManager<T> : IRegisterInstanceManager<T> where T :
|
||||
var instance = registeredInstance ?? new InstanceRegistration
|
||||
{
|
||||
InstanceRegistrationId = instanceId,
|
||||
WorkerTypeName = typeof(T).Name
|
||||
WorkerTypeName = typeof(T).GetFormattedName()
|
||||
};
|
||||
|
||||
instance.LastUpdated = DateTime.UtcNow;
|
||||
|
@ -24,7 +24,7 @@
|
||||
// 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
|
||||
|
||||
namespace ASC.Core.Common.Hosting;
|
||||
namespace ASC.Core.Common.Hosting;
|
||||
|
||||
[Singletone]
|
||||
public class RegisterInstanceWorkerService<T> : BackgroundService where T : IHostedService
|
||||
@ -33,7 +33,7 @@ public class RegisterInstanceWorkerService<T> : BackgroundService where T : IHos
|
||||
private readonly IHostApplicationLifetime _applicationLifetime;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
public static readonly string InstanceId =
|
||||
$"{typeof(T).Name}_{DateTime.UtcNow.Ticks}";
|
||||
$"{typeof(T).GetFormattedName()}_{DateTime.UtcNow.Ticks}";
|
||||
|
||||
public RegisterInstanceWorkerService(
|
||||
ILogger<RegisterInstanceWorkerService<T>> logger,
|
||||
@ -58,7 +58,7 @@ public class RegisterInstanceWorkerService<T> : BackgroundService where T : IHos
|
||||
await registerInstanceService.Register(InstanceId);
|
||||
await registerInstanceService.DeleteOrphanInstances();
|
||||
|
||||
_logger.InformationWorkingRunnging(DateTimeOffset.Now);
|
||||
_logger.TraceWorkingRunnging(DateTimeOffset.Now);
|
||||
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
@ -87,5 +87,6 @@ public class RegisterInstanceWorkerService<T> : BackgroundService where T : IHos
|
||||
}
|
||||
|
||||
await base.StopAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ internal static partial class RegisterInstanceWorkerServiceLogger
|
||||
[LoggerMessage(Level = LogLevel.Trace, Message = "DbUpdateConcurrencyException: then updating {instanceName} at {time} time.")]
|
||||
public static partial void TraceDbUpdateConcurrencyException(this ILogger logger, string instanceName, DateTimeOffset time);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Information, Message = "Worker running at: {time}")]
|
||||
public static partial void InformationWorkingRunnging(this ILogger logger, DateTimeOffset time);
|
||||
[LoggerMessage(Level = LogLevel.Trace, Message = "Worker running at: {time}")]
|
||||
public static partial void TraceWorkingRunnging(this ILogger logger, DateTimeOffset time);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Information, Message = "UnRegister Instance {instanceName} running at: {time}.")]
|
||||
public static partial void InformationUnRegister(this ILogger logger, string instanceName, DateTimeOffset time);
|
||||
|
@ -26,16 +26,32 @@
|
||||
|
||||
namespace ASC.Web.Files.Services.WCFService.FileOperations;
|
||||
|
||||
[ProtoContract]
|
||||
[ProtoInclude(100, typeof(FileConverterOperationResult))]
|
||||
public class FileOperationResult
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonPropertyName("operation")]
|
||||
[ProtoMember(2)]
|
||||
public FileOperationType OperationType { get; set; }
|
||||
|
||||
[ProtoMember(3)]
|
||||
public int Progress { get; set; }
|
||||
|
||||
[ProtoMember(4)]
|
||||
public string Source { get; set; }
|
||||
|
||||
[ProtoMember(5)]
|
||||
public string Result { get; set; }
|
||||
|
||||
[ProtoMember(6)]
|
||||
public string Error { get; set; }
|
||||
|
||||
[ProtoMember(7)]
|
||||
public string Processed { get; set; }
|
||||
|
||||
[ProtoMember(8)]
|
||||
public bool Finished { get; set; }
|
||||
}
|
||||
|
@ -24,44 +24,40 @@
|
||||
// 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
|
||||
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
|
||||
using Timeout = System.Threading.Timeout;
|
||||
|
||||
namespace ASC.Web.Files.Utils;
|
||||
|
||||
[Singletone(Additional = typeof(FileConverterQueueExtension))]
|
||||
internal class FileConverterQueue<T> : IDisposable
|
||||
[Singletone]
|
||||
public class FileConverterQueue<T>
|
||||
{
|
||||
private readonly object _singleThread = new object();
|
||||
private readonly IDictionary<File<T>, ConvertFileOperationResult> _conversionQueue;
|
||||
private readonly Timer _timer;
|
||||
private readonly object _locker;
|
||||
private readonly ICache _cache;
|
||||
private const int _timerPeriod = 500;
|
||||
private readonly object _locker = new object();
|
||||
private readonly IDistributedCache _distributedCache;
|
||||
private readonly string _name = "asc_file_converter_queue_";
|
||||
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
|
||||
public FileConverterQueue(IServiceScopeFactory serviceScopeFactory, ICache cache)
|
||||
public FileConverterQueue(IDistributedCache distributedCache)
|
||||
{
|
||||
_conversionQueue = new Dictionary<File<T>, ConvertFileOperationResult>(new FileComparer<T>());
|
||||
_timer = new Timer(CheckConvertFilesStatus, null, 0, Timeout.Infinite);
|
||||
_locker = new object();
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_cache = cache;
|
||||
_distributedCache = distributedCache;
|
||||
}
|
||||
|
||||
public void Add(File<T> file, string password, int tenantId, IAccount account, bool deleteAfter, string url, string serverRootPath)
|
||||
public void Add(File<T> file,
|
||||
string password,
|
||||
int tenantId,
|
||||
IAccount account,
|
||||
bool deleteAfter,
|
||||
string url,
|
||||
string serverRootPath)
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
if (_conversionQueue.ContainsKey(file))
|
||||
if (Contains(file))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var queueResult = new ConvertFileOperationResult
|
||||
var queueResult = new FileConverterOperationResult
|
||||
{
|
||||
Source = string.Format("{{\"id\":\"{0}\", \"version\":\"{1}\"}}", file.Id, file.Version),
|
||||
Source = System.Text.Json.JsonSerializer.Serialize(new { id = file.Id, version = file.Version }),
|
||||
OperationType = FileOperationType.Convert,
|
||||
Error = string.Empty,
|
||||
Progress = 0,
|
||||
@ -69,316 +65,82 @@ internal class FileConverterQueue<T> : IDisposable
|
||||
Processed = "",
|
||||
Id = string.Empty,
|
||||
TenantId = tenantId,
|
||||
Account = account,
|
||||
Account = account.ID,
|
||||
Delete = deleteAfter,
|
||||
StartDateTime = DateTime.Now,
|
||||
Url = url,
|
||||
Password = password,
|
||||
ServerRootPath = serverRootPath
|
||||
};
|
||||
_conversionQueue.Add(file, queueResult);
|
||||
_cache.Insert(GetKey(file), queueResult, TimeSpan.FromMinutes(10));
|
||||
|
||||
_timer.Change(0, Timeout.Infinite);
|
||||
|
||||
Enqueue(queueResult);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Enqueue(FileConverterOperationResult val)
|
||||
{
|
||||
var fromCache = LoadFromCache().ToList();
|
||||
|
||||
fromCache.Add(val);
|
||||
|
||||
SaveToCache(fromCache);
|
||||
}
|
||||
|
||||
public void Dequeue(File<T> file)
|
||||
{
|
||||
var fromCache = LoadFromCache().ToList();
|
||||
var task = PeekTask(file);
|
||||
|
||||
fromCache.Remove(task);
|
||||
|
||||
SaveToCache(fromCache);
|
||||
}
|
||||
|
||||
public async Task<ConvertFileOperationResult> GetStatusAsync(KeyValuePair<File<T>, bool> pair, FileSecurity fileSecurity)
|
||||
public async Task<FileConverterOperationResult> GetStatusAsync(KeyValuePair<File<T>, bool> pair, FileSecurity fileSecurity)
|
||||
{
|
||||
var file = pair.Key;
|
||||
var key = GetKey(file);
|
||||
var operation = _cache.Get<ConvertFileOperationResult>(key);
|
||||
var operation = PeekTask(pair.Key);
|
||||
|
||||
if (operation != null && (pair.Value || await fileSecurity.CanReadAsync(file)))
|
||||
{
|
||||
lock (_locker)
|
||||
if (operation.Progress == 100)
|
||||
{
|
||||
if (operation.Progress == 100)
|
||||
{
|
||||
_conversionQueue.Remove(file);
|
||||
_cache.Remove(key);
|
||||
}
|
||||
|
||||
return operation;
|
||||
Dequeue(pair.Key);
|
||||
}
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public FileConverterOperationResult PeekTask(File<T> file)
|
||||
{
|
||||
var exist = LoadFromCache();
|
||||
|
||||
return exist.FirstOrDefault(x =>
|
||||
{
|
||||
var fileId = JsonDocument.Parse(x.Source).RootElement.GetProperty("id").Deserialize<T>();
|
||||
var fileVersion = JsonDocument.Parse(x.Source).RootElement.GetProperty("version").Deserialize<int>();
|
||||
|
||||
return file.Id.ToString() == fileId.ToString() && file.Version == fileVersion;
|
||||
});
|
||||
}
|
||||
|
||||
public bool IsConverting(File<T> file)
|
||||
{
|
||||
var result = _cache.Get<ConvertFileOperationResult>(GetKey(file));
|
||||
var result = PeekTask(file);
|
||||
|
||||
return result != null && result.Progress != 100 && string.IsNullOrEmpty(result.Error);
|
||||
}
|
||||
|
||||
private void CheckConvertFilesStatus(object _)
|
||||
{
|
||||
if (Monitor.TryEnter(_singleThread))
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
TenantManager tenantManager;
|
||||
UserManager userManager;
|
||||
SecurityContext securityContext;
|
||||
IDaoFactory daoFactory;
|
||||
FileSecurity fileSecurity;
|
||||
PathProvider pathProvider;
|
||||
SetupInfo setupInfo;
|
||||
FileUtility fileUtility;
|
||||
DocumentServiceHelper documentServiceHelper;
|
||||
DocumentServiceConnector documentServiceConnector;
|
||||
EntryStatusManager entryManager;
|
||||
FileConverter fileConverter;
|
||||
|
||||
var logger = scope.ServiceProvider.GetService<ILogger<FileConverterQueue<T>>>();
|
||||
|
||||
try
|
||||
{
|
||||
var filesIsConverting = new List<File<T>>();
|
||||
lock (_locker)
|
||||
{
|
||||
_timer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
|
||||
var queues = _conversionQueue.Where(x => !string.IsNullOrEmpty(x.Value.Processed)
|
||||
&& (x.Value.Progress == 100 && DateTime.UtcNow - x.Value.StopDateTime > TimeSpan.FromMinutes(1) ||
|
||||
DateTime.UtcNow - x.Value.StopDateTime > TimeSpan.FromMinutes(10)))
|
||||
.ToList();
|
||||
|
||||
foreach (var q in queues)
|
||||
{
|
||||
_conversionQueue.Remove(q);
|
||||
_cache.Remove(GetKey(q.Key));
|
||||
}
|
||||
|
||||
logger.DebugRunCheckConvertFilesStatus(_conversionQueue.Count);
|
||||
|
||||
if (_conversionQueue.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
filesIsConverting = _conversionQueue
|
||||
.Where(x => string.IsNullOrEmpty(x.Value.Processed))
|
||||
.Select(x => x.Key)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
string convertedFileUrl = null;
|
||||
|
||||
foreach (var file in filesIsConverting)
|
||||
{
|
||||
var fileUri = file.Id.ToString();
|
||||
int operationResultProgress;
|
||||
|
||||
try
|
||||
{
|
||||
int tenantId;
|
||||
IAccount account;
|
||||
string password;
|
||||
string serverRootPath;
|
||||
|
||||
lock (_locker)
|
||||
{
|
||||
if (!_conversionQueue.ContainsKey(file))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var operationResult = _conversionQueue[file];
|
||||
if (!string.IsNullOrEmpty(operationResult.Processed))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
operationResult.Processed = "1";
|
||||
tenantId = operationResult.TenantId;
|
||||
account = operationResult.Account;
|
||||
password = operationResult.Password;
|
||||
serverRootPath = operationResult.ServerRootPath;
|
||||
|
||||
//if (HttpContext.Current == null && !WorkContext.IsMono)
|
||||
//{
|
||||
// HttpContext.Current = new HttpContext(
|
||||
// new HttpRequest("hack", operationResult.Url, string.Empty),
|
||||
// new HttpResponse(new StringWriter()));
|
||||
//}
|
||||
|
||||
_cache.Insert(GetKey(file), operationResult, TimeSpan.FromMinutes(10));
|
||||
}
|
||||
|
||||
var commonLinkUtilitySettings = scope.ServiceProvider.GetService<CommonLinkUtilitySettings>();
|
||||
commonLinkUtilitySettings.ServerUri = serverRootPath;
|
||||
|
||||
var scopeClass = scope.ServiceProvider.GetService<FileConverterQueueScope>();
|
||||
(_, tenantManager, userManager, securityContext, daoFactory, fileSecurity, pathProvider, setupInfo, fileUtility, documentServiceHelper, documentServiceConnector, entryManager, fileConverter) = scopeClass;
|
||||
|
||||
tenantManager.SetCurrentTenant(tenantId);
|
||||
|
||||
securityContext.AuthenticateMeWithoutCookie(account);
|
||||
|
||||
var user = userManager.GetUsers(account.ID);
|
||||
var culture = string.IsNullOrEmpty(user.CultureName) ? tenantManager.GetCurrentTenant().GetCulture() : CultureInfo.GetCultureInfo(user.CultureName);
|
||||
Thread.CurrentThread.CurrentCulture = culture;
|
||||
Thread.CurrentThread.CurrentUICulture = culture;
|
||||
|
||||
if (!fileSecurity.CanReadAsync(file).Result && file.RootFolderType != FolderType.BUNCH)
|
||||
{
|
||||
//No rights in CRM after upload before attach
|
||||
throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_ReadFile);
|
||||
}
|
||||
if (file.ContentLength > setupInfo.AvailableFileSize)
|
||||
{
|
||||
throw new Exception(string.Format(FilesCommonResource.ErrorMassage_FileSizeConvert, FileSizeComment.FilesSizeToString(setupInfo.AvailableFileSize)));
|
||||
}
|
||||
|
||||
fileUri = pathProvider.GetFileStreamUrl(file);
|
||||
|
||||
var toExtension = fileUtility.GetInternalExtension(file.Title);
|
||||
var fileExtension = file.ConvertedExtension;
|
||||
var docKey = documentServiceHelper.GetDocKey(file);
|
||||
|
||||
fileUri = documentServiceConnector.ReplaceCommunityAdress(fileUri);
|
||||
(operationResultProgress, convertedFileUrl) = documentServiceConnector.GetConvertedUriAsync(fileUri, fileExtension, toExtension, docKey, password, null, null, true).Result;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var password = exception.InnerException is DocumentServiceException documentServiceException
|
||||
&& documentServiceException.Code == DocumentServiceException.ErrorCode.ConvertPassword;
|
||||
|
||||
logger.ErrorConvertFileWithUrl(file.Id.ToString(), fileUri, exception);
|
||||
lock (_locker)
|
||||
{
|
||||
if (_conversionQueue.TryGetValue(file, out var operationResult))
|
||||
{
|
||||
if (operationResult.Delete)
|
||||
{
|
||||
_conversionQueue.Remove(file);
|
||||
_cache.Remove(GetKey(file));
|
||||
}
|
||||
else
|
||||
{
|
||||
operationResult.Progress = 100;
|
||||
operationResult.StopDateTime = DateTime.UtcNow;
|
||||
operationResult.Error = exception.Message;
|
||||
if (password)
|
||||
{
|
||||
operationResult.Result = "password";
|
||||
}
|
||||
|
||||
_cache.Insert(GetKey(file), operationResult, TimeSpan.FromMinutes(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
operationResultProgress = Math.Min(operationResultProgress, 100);
|
||||
if (operationResultProgress < 100)
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
if (_conversionQueue.TryGetValue(file, out var operationResult))
|
||||
{
|
||||
if (DateTime.Now - operationResult.StartDateTime > TimeSpan.FromMinutes(10))
|
||||
{
|
||||
operationResult.StopDateTime = DateTime.UtcNow;
|
||||
operationResult.Error = FilesCommonResource.ErrorMassage_ConvertTimeout;
|
||||
logger.ErrorCheckConvertFilesStatus(file.Id.ToString(), file.ContentLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
operationResult.Processed = "";
|
||||
}
|
||||
operationResult.Progress = operationResultProgress;
|
||||
_cache.Insert(GetKey(file), operationResult, TimeSpan.FromMinutes(10));
|
||||
}
|
||||
}
|
||||
|
||||
logger.DebugCheckConvertFilesStatusIterationContinue();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
File<T> newFile = null;
|
||||
var operationResultError = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
newFile = fileConverter.SaveConvertedFileAsync(file, convertedFileUrl).Result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
operationResultError = e.Message;
|
||||
|
||||
logger.ErrorOperation(operationResultError, convertedFileUrl, fileUri, e);
|
||||
|
||||
continue;
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
if (_conversionQueue.TryGetValue(file, out var operationResult))
|
||||
{
|
||||
if (operationResult.Delete)
|
||||
{
|
||||
_conversionQueue.Remove(file);
|
||||
_cache.Remove(GetKey(file));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newFile != null)
|
||||
{
|
||||
var folderDao = daoFactory.GetFolderDao<T>();
|
||||
var folder = folderDao.GetFolderAsync(newFile.ParentId).Result;
|
||||
var folderTitle = fileSecurity.CanReadAsync(folder).Result ? folder.Title : null;
|
||||
operationResult.Result = FileJsonSerializerAsync(entryManager, newFile, folderTitle).Result;
|
||||
}
|
||||
|
||||
operationResult.Progress = 100;
|
||||
operationResult.StopDateTime = DateTime.UtcNow;
|
||||
operationResult.Processed = "1";
|
||||
if (!string.IsNullOrEmpty(operationResultError))
|
||||
{
|
||||
operationResult.Error = operationResultError;
|
||||
}
|
||||
|
||||
_cache.Insert(GetKey(file), operationResult, TimeSpan.FromMinutes(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.DebugCheckConvertFilesStatusIterationEnd();
|
||||
}
|
||||
|
||||
lock (_locker)
|
||||
{
|
||||
_timer.Change(_timerPeriod, _timerPeriod);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.ErrorWithException(exception);
|
||||
lock (_locker)
|
||||
{
|
||||
_timer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(_singleThread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetKey(File<T> f)
|
||||
{
|
||||
return string.Format("fileConvertation-{0}", f.Id);
|
||||
}
|
||||
|
||||
internal async Task<string> FileJsonSerializerAsync(EntryStatusManager EntryManager, File<T> file, string folderTitle)
|
||||
public async Task<string> FileJsonSerializerAsync(EntryStatusManager EntryManager, File<T> file, string folderTitle)
|
||||
{
|
||||
if (file == null)
|
||||
{
|
||||
@ -406,94 +168,85 @@ internal class FileConverterQueue<T> : IDisposable
|
||||
}, options);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
private bool Contains(File<T> file)
|
||||
{
|
||||
if (_timer != null)
|
||||
var queueTasks = LoadFromCache();
|
||||
|
||||
return queueTasks.Any(x =>
|
||||
{
|
||||
_timer.Dispose();
|
||||
var fileId = JsonDocument.Parse(x.Source).RootElement.GetProperty("id").Deserialize<T>();
|
||||
var fileVersion = JsonDocument.Parse(x.Source).RootElement.GetProperty("version").Deserialize<int>();
|
||||
|
||||
return file.Id.ToString() == fileId.ToString() && file.Version == fileVersion;
|
||||
});
|
||||
}
|
||||
|
||||
private bool IsOrphanCacheItem(FileConverterOperationResult x)
|
||||
{
|
||||
return !string.IsNullOrEmpty(x.Processed)
|
||||
&& (x.Progress == 100 && DateTime.UtcNow - x.StopDateTime > TimeSpan.FromMinutes(1) ||
|
||||
DateTime.UtcNow - x.StopDateTime > TimeSpan.FromMinutes(10));
|
||||
}
|
||||
|
||||
private IEnumerable<FileConverterOperationResult> DeleteOrphanCacheItem(IEnumerable<FileConverterOperationResult> queueTasks)
|
||||
{
|
||||
var listTasks = queueTasks.ToList();
|
||||
|
||||
listTasks.RemoveAll(IsOrphanCacheItem);
|
||||
|
||||
SaveToCache(listTasks);
|
||||
|
||||
return queueTasks;
|
||||
}
|
||||
|
||||
public IEnumerable<FileConverterOperationResult> GetAllTaskStatus()
|
||||
{
|
||||
var queueTasks = LoadFromCache();
|
||||
|
||||
queueTasks = DeleteOrphanCacheItem(queueTasks);
|
||||
|
||||
return queueTasks;
|
||||
}
|
||||
|
||||
public void SaveToCache(IEnumerable<FileConverterOperationResult> queueTasks)
|
||||
{
|
||||
if (!queueTasks.Any())
|
||||
{
|
||||
_distributedCache.Remove(GetKey());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
|
||||
ProtoBuf.Serializer.Serialize(ms, queueTasks);
|
||||
|
||||
_distributedCache.Set(GetKey(), ms.ToArray(), new DistributedCacheEntryOptions
|
||||
{
|
||||
SlidingExpiration = TimeSpan.FromMinutes(15)
|
||||
});
|
||||
}
|
||||
|
||||
private string GetKey()
|
||||
{
|
||||
return $"{_name}_{typeof(T).Name}".ToLowerInvariant();
|
||||
}
|
||||
|
||||
private IEnumerable<FileConverterOperationResult> LoadFromCache()
|
||||
{
|
||||
var serializedObject = _distributedCache.Get(GetKey());
|
||||
|
||||
if (serializedObject == null)
|
||||
{
|
||||
return new List<FileConverterOperationResult>();
|
||||
}
|
||||
|
||||
using var ms = new MemoryStream(serializedObject);
|
||||
|
||||
return ProtoBuf.Serializer.Deserialize<List<FileConverterOperationResult>>(ms);
|
||||
}
|
||||
}
|
||||
|
||||
[Scope]
|
||||
public class FileConverterQueueScope
|
||||
{
|
||||
private readonly ILogger _options;
|
||||
private readonly TenantManager _tenantManager;
|
||||
private readonly UserManager _userManager;
|
||||
private readonly SecurityContext _securityContext;
|
||||
private readonly IDaoFactory _daoFactory;
|
||||
private readonly FileSecurity _fileSecurity;
|
||||
private readonly PathProvider _pathProvider;
|
||||
private readonly SetupInfo _setupInfo;
|
||||
private readonly FileUtility _fileUtility;
|
||||
private readonly DocumentServiceHelper _documentServiceHelper;
|
||||
private readonly DocumentServiceConnector _documentServiceConnector;
|
||||
private readonly EntryStatusManager _entryManager;
|
||||
private readonly FileConverter _fileConverter;
|
||||
|
||||
public FileConverterQueueScope(
|
||||
ILogger<FileConverterQueueScope> options,
|
||||
TenantManager tenantManager,
|
||||
UserManager userManager,
|
||||
SecurityContext securityContext,
|
||||
IDaoFactory daoFactory,
|
||||
FileSecurity fileSecurity,
|
||||
PathProvider pathProvider,
|
||||
SetupInfo setupInfo,
|
||||
FileUtility fileUtility,
|
||||
DocumentServiceHelper documentServiceHelper,
|
||||
DocumentServiceConnector documentServiceConnector,
|
||||
EntryStatusManager entryManager,
|
||||
FileConverter fileConverter)
|
||||
{
|
||||
_options = options;
|
||||
_tenantManager = tenantManager;
|
||||
_userManager = userManager;
|
||||
_securityContext = securityContext;
|
||||
_daoFactory = daoFactory;
|
||||
_fileSecurity = fileSecurity;
|
||||
_pathProvider = pathProvider;
|
||||
_setupInfo = setupInfo;
|
||||
_fileUtility = fileUtility;
|
||||
_documentServiceHelper = documentServiceHelper;
|
||||
_documentServiceConnector = documentServiceConnector;
|
||||
_entryManager = entryManager;
|
||||
_fileConverter = fileConverter;
|
||||
}
|
||||
|
||||
|
||||
public void Deconstruct(out ILogger optionsMonitor,
|
||||
out TenantManager tenantManager,
|
||||
out UserManager userManager,
|
||||
out SecurityContext securityContext,
|
||||
out IDaoFactory daoFactory,
|
||||
out FileSecurity fileSecurity,
|
||||
out PathProvider pathProvider,
|
||||
out SetupInfo setupInfo,
|
||||
out FileUtility fileUtility,
|
||||
out DocumentServiceHelper documentServiceHelper,
|
||||
out DocumentServiceConnector documentServiceConnector,
|
||||
out EntryStatusManager entryManager,
|
||||
out FileConverter fileConverter)
|
||||
{
|
||||
optionsMonitor = _options;
|
||||
tenantManager = _tenantManager;
|
||||
userManager = _userManager;
|
||||
securityContext = _securityContext;
|
||||
daoFactory = _daoFactory;
|
||||
fileSecurity = _fileSecurity;
|
||||
pathProvider = _pathProvider;
|
||||
setupInfo = _setupInfo;
|
||||
fileUtility = _fileUtility;
|
||||
documentServiceHelper = _documentServiceHelper;
|
||||
documentServiceConnector = _documentServiceConnector;
|
||||
entryManager = _entryManager;
|
||||
fileConverter = _fileConverter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class FileJsonSerializerData<T>
|
||||
{
|
||||
public T Id { get; set; }
|
||||
@ -710,9 +463,9 @@ public class FileConverter
|
||||
var uriTuple = await _documentServiceConnector.GetConvertedUriAsync(fileUri, fileExtension, toExtension, docKey, null, null, null, false);
|
||||
var convertUri = uriTuple.ConvertedDocumentUri;
|
||||
|
||||
var operationResult = new ConvertFileOperationResult
|
||||
var operationResult = new FileConverterOperationResult
|
||||
{
|
||||
Source = string.Format("{{\"id\":\"{0}\", \"version\":\"{1}\"}}", file.Id, file.Version),
|
||||
Source = System.Text.Json.JsonSerializer.Serialize(new { id = file.Id, version = file.Version }),
|
||||
OperationType = FileOperationType.Convert,
|
||||
Error = string.Empty,
|
||||
Progress = 0,
|
||||
@ -720,7 +473,7 @@ public class FileConverter
|
||||
Processed = "",
|
||||
Id = string.Empty,
|
||||
TenantId = _tenantManager.GetCurrentTenant().Id,
|
||||
Account = _authContext.CurrentAccount,
|
||||
Account = _authContext.CurrentAccount.ID,
|
||||
Delete = false,
|
||||
StartDateTime = DateTime.Now,
|
||||
Url = _httpContextAccesor?.HttpContext != null ? _httpContextAccesor.HttpContext.Request.GetUrlRewriter().ToString() : null,
|
||||
@ -918,27 +671,6 @@ internal class FileComparer<T> : IEqualityComparer<File<T>>
|
||||
}
|
||||
}
|
||||
|
||||
internal class ConvertFileOperationResult : FileOperationResult
|
||||
{
|
||||
public DateTime StartDateTime { get; set; }
|
||||
public DateTime StopDateTime { get; set; }
|
||||
public int TenantId { get; set; }
|
||||
public IAccount Account { get; set; }
|
||||
public bool Delete { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
//hack for download
|
||||
public string ServerRootPath { get; set; }
|
||||
}
|
||||
|
||||
public static class FileConverterQueueExtension
|
||||
{
|
||||
public static void Register(DIHelper services)
|
||||
{
|
||||
services.TryAdd<FileConverterQueueScope>();
|
||||
}
|
||||
}
|
||||
|
||||
public static class FileConverterExtension
|
||||
{
|
||||
|
@ -0,0 +1,56 @@
|
||||
// (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
|
||||
|
||||
namespace ASC.Web.Files.Utils;
|
||||
|
||||
[ProtoContract]
|
||||
public class FileConverterOperationResult : FileOperationResult
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public DateTime StartDateTime { get; set; }
|
||||
|
||||
[ProtoMember(2)]
|
||||
public DateTime StopDateTime { get; set; }
|
||||
|
||||
[ProtoMember(3)]
|
||||
public int TenantId { get; set; }
|
||||
|
||||
[ProtoMember(4)]
|
||||
public Guid Account { get; set; }
|
||||
|
||||
[ProtoMember(5)]
|
||||
public bool Delete { get; set; }
|
||||
|
||||
[ProtoMember(6)]
|
||||
public string Url { get; set; }
|
||||
|
||||
[ProtoMember(7)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[ProtoMember(8)]
|
||||
//hack for download
|
||||
public string ServerRootPath { get; set; }
|
||||
}
|
355
products/ASC.Files/Service/FileConverterService.cs
Normal file
355
products/ASC.Files/Service/FileConverterService.cs
Normal file
@ -0,0 +1,355 @@
|
||||
// (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
|
||||
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
|
||||
using ASC.Common.Log;
|
||||
using ASC.Core.Common.Hosting;
|
||||
using ASC.Core.Common.Hosting.Interfaces;
|
||||
using ASC.Files.Core.Log;
|
||||
using ASC.Files.Core.Resources;
|
||||
using ASC.Web.Files.Utils;
|
||||
using ASC.Web.Studio.Core;
|
||||
|
||||
using static ASC.Web.Core.Files.DocumentService;
|
||||
|
||||
namespace ASC.Files.ThumbnailBuilder;
|
||||
|
||||
[Singletone(Additional = typeof(FileConverterQueueExtension))]
|
||||
internal class FileConverterService<T> : BackgroundService
|
||||
{
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly int _timerDelay = 1000;
|
||||
|
||||
public FileConverterService(
|
||||
IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
using var serviceScope = _serviceScopeFactory.CreateScope();
|
||||
|
||||
var registerInstanceService = serviceScope.ServiceProvider.GetService<IRegisterInstanceManager<FileConverterService<T>>>();
|
||||
|
||||
if (!await registerInstanceService.IsActive(RegisterInstanceWorkerService<FileConverterService<T>>.InstanceId))
|
||||
{
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
await ExecuteCheckFileConverterStatus(serviceScope);
|
||||
|
||||
await Task.Delay(_timerDelay, stoppingToken);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task ExecuteCheckFileConverterStatus(IServiceScope scope)
|
||||
{
|
||||
TenantManager tenantManager;
|
||||
UserManager userManager;
|
||||
SecurityContext securityContext;
|
||||
IDaoFactory daoFactory;
|
||||
FileSecurity fileSecurity;
|
||||
PathProvider pathProvider;
|
||||
SetupInfo setupInfo;
|
||||
FileUtility fileUtility;
|
||||
DocumentServiceHelper documentServiceHelper;
|
||||
DocumentServiceConnector documentServiceConnector;
|
||||
EntryStatusManager entryManager;
|
||||
FileConverter fileConverter;
|
||||
FileConverterQueue<T> fileConverterQueue;
|
||||
|
||||
var logger = scope.ServiceProvider.GetService<ILogger<FileConverterQueue<T>>>();
|
||||
|
||||
try
|
||||
{
|
||||
var scopeClass = scope.ServiceProvider.GetService<FileConverterQueueScope>();
|
||||
(_, tenantManager, userManager, securityContext, daoFactory, fileSecurity, pathProvider, setupInfo, fileUtility, documentServiceHelper, documentServiceConnector, entryManager, fileConverter) = scopeClass;
|
||||
|
||||
fileConverterQueue = scope.ServiceProvider.GetService<FileConverterQueue<T>>();
|
||||
|
||||
var _conversionQueue = fileConverterQueue.GetAllTaskStatus().ToList();
|
||||
|
||||
|
||||
var filesIsConverting = _conversionQueue
|
||||
.Where(x => string.IsNullOrEmpty(x.Processed))
|
||||
.ToList();
|
||||
|
||||
foreach (var converter in filesIsConverting)
|
||||
{
|
||||
var fileId = JsonDocument.Parse(converter.Source).RootElement.GetProperty("id").Deserialize<T>();
|
||||
var fileVersion = JsonDocument.Parse(converter.Source).RootElement.GetProperty("version").Deserialize<int>();
|
||||
|
||||
int operationResultProgress;
|
||||
var password = converter.Password;
|
||||
|
||||
var commonLinkUtilitySettings = scope.ServiceProvider.GetService<CommonLinkUtilitySettings>();
|
||||
commonLinkUtilitySettings.ServerUri = converter.ServerRootPath;
|
||||
|
||||
tenantManager.SetCurrentTenant(converter.TenantId);
|
||||
|
||||
securityContext.AuthenticateMeWithoutCookie(converter.Account);
|
||||
|
||||
var file = await daoFactory.GetFileDao<T>().GetFileAsync(fileId, fileVersion);
|
||||
var fileUri = file.Id.ToString();
|
||||
|
||||
string convertedFileUrl;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
var user = userManager.GetUsers(converter.Account);
|
||||
|
||||
var culture = string.IsNullOrEmpty(user.CultureName) ? tenantManager.GetCurrentTenant().GetCulture() : CultureInfo.GetCultureInfo(user.CultureName);
|
||||
|
||||
Thread.CurrentThread.CurrentCulture = culture;
|
||||
Thread.CurrentThread.CurrentUICulture = culture;
|
||||
|
||||
if (!fileSecurity.CanReadAsync(file).Result && file.RootFolderType != FolderType.BUNCH)
|
||||
{
|
||||
//No rights in CRM after upload before attach
|
||||
throw new System.Security.SecurityException(FilesCommonResource.ErrorMassage_SecurityException_ReadFile);
|
||||
}
|
||||
|
||||
if (file.ContentLength > setupInfo.AvailableFileSize)
|
||||
{
|
||||
throw new Exception(string.Format(FilesCommonResource.ErrorMassage_FileSizeConvert, FileSizeComment.FilesSizeToString(setupInfo.AvailableFileSize)));
|
||||
}
|
||||
|
||||
fileUri = pathProvider.GetFileStreamUrl(file);
|
||||
|
||||
var toExtension = fileUtility.GetInternalExtension(file.Title);
|
||||
var fileExtension = file.ConvertedExtension;
|
||||
var docKey = documentServiceHelper.GetDocKey(file);
|
||||
|
||||
fileUri = documentServiceConnector.ReplaceCommunityAdress(fileUri);
|
||||
(operationResultProgress, convertedFileUrl) = documentServiceConnector.GetConvertedUriAsync(fileUri, fileExtension, toExtension, docKey, password, null, null, true).Result;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var password1 = exception.InnerException is DocumentServiceException documentServiceException
|
||||
&& documentServiceException.Code == DocumentServiceException.ErrorCode.ConvertPassword;
|
||||
|
||||
logger.ErrorConvertFileWithUrl(file.Id.ToString(), fileUri, exception);
|
||||
|
||||
var operationResult = converter;
|
||||
|
||||
if (operationResult.Delete)
|
||||
{
|
||||
_conversionQueue.Remove(operationResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
operationResult.Progress = 100;
|
||||
operationResult.StopDateTime = DateTime.UtcNow;
|
||||
operationResult.Error = exception.Message;
|
||||
|
||||
if (password1)
|
||||
{
|
||||
operationResult.Result = "password";
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
operationResultProgress = Math.Min(operationResultProgress, 100);
|
||||
|
||||
if (operationResultProgress < 100)
|
||||
{
|
||||
var operationResult = converter;
|
||||
|
||||
if (DateTime.Now - operationResult.StartDateTime > TimeSpan.FromMinutes(10))
|
||||
{
|
||||
operationResult.StopDateTime = DateTime.UtcNow;
|
||||
operationResult.Error = FilesCommonResource.ErrorMassage_ConvertTimeout;
|
||||
|
||||
logger.ErrorCheckConvertFilesStatus(file.Id.ToString(), file.ContentLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
operationResult.Processed = "";
|
||||
}
|
||||
|
||||
operationResult.Progress = operationResultProgress;
|
||||
|
||||
logger.DebugCheckConvertFilesStatusIterationContinue();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
File<T> newFile = null;
|
||||
|
||||
var operationResultError = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
newFile = fileConverter.SaveConvertedFileAsync(file, convertedFileUrl).Result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
operationResultError = e.Message;
|
||||
|
||||
logger.ErrorOperation(operationResultError, convertedFileUrl, fileUri, e);
|
||||
|
||||
continue;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var operationResult = converter;
|
||||
|
||||
if (operationResult.Delete)
|
||||
{
|
||||
_conversionQueue.Remove(operationResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newFile != null)
|
||||
{
|
||||
var folderDao = daoFactory.GetFolderDao<T>();
|
||||
var folder = folderDao.GetFolderAsync(newFile.ParentId).Result;
|
||||
var folderTitle = fileSecurity.CanReadAsync(folder).Result ? folder.Title : null;
|
||||
|
||||
operationResult.Result = fileConverterQueue.FileJsonSerializerAsync(entryManager, newFile, folderTitle).Result;
|
||||
}
|
||||
|
||||
operationResult.Progress = 100;
|
||||
operationResult.StopDateTime = DateTime.UtcNow;
|
||||
operationResult.Processed = "1";
|
||||
|
||||
if (!string.IsNullOrEmpty(operationResultError))
|
||||
{
|
||||
operationResult.Error = operationResultError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.DebugCheckConvertFilesStatusIterationEnd();
|
||||
}
|
||||
|
||||
fileConverterQueue.SaveToCache(_conversionQueue);
|
||||
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.ErrorWithException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class FileConverterQueueExtension
|
||||
{
|
||||
public static void Register(DIHelper services)
|
||||
{
|
||||
services.TryAdd<FileConverterQueueScope>();
|
||||
}
|
||||
}
|
||||
|
||||
[Scope]
|
||||
public class FileConverterQueueScope
|
||||
{
|
||||
private readonly ILogger _options;
|
||||
private readonly TenantManager _tenantManager;
|
||||
private readonly UserManager _userManager;
|
||||
private readonly SecurityContext _securityContext;
|
||||
private readonly IDaoFactory _daoFactory;
|
||||
private readonly FileSecurity _fileSecurity;
|
||||
private readonly PathProvider _pathProvider;
|
||||
private readonly SetupInfo _setupInfo;
|
||||
private readonly FileUtility _fileUtility;
|
||||
private readonly DocumentServiceHelper _documentServiceHelper;
|
||||
private readonly DocumentServiceConnector _documentServiceConnector;
|
||||
private readonly EntryStatusManager _entryManager;
|
||||
private readonly FileConverter _fileConverter;
|
||||
|
||||
public FileConverterQueueScope(
|
||||
ILogger<FileConverterQueueScope> options,
|
||||
TenantManager tenantManager,
|
||||
UserManager userManager,
|
||||
SecurityContext securityContext,
|
||||
IDaoFactory daoFactory,
|
||||
FileSecurity fileSecurity,
|
||||
PathProvider pathProvider,
|
||||
SetupInfo setupInfo,
|
||||
FileUtility fileUtility,
|
||||
DocumentServiceHelper documentServiceHelper,
|
||||
DocumentServiceConnector documentServiceConnector,
|
||||
EntryStatusManager entryManager,
|
||||
FileConverter fileConverter)
|
||||
{
|
||||
_options = options;
|
||||
_tenantManager = tenantManager;
|
||||
_userManager = userManager;
|
||||
_securityContext = securityContext;
|
||||
_daoFactory = daoFactory;
|
||||
_fileSecurity = fileSecurity;
|
||||
_pathProvider = pathProvider;
|
||||
_setupInfo = setupInfo;
|
||||
_fileUtility = fileUtility;
|
||||
_documentServiceHelper = documentServiceHelper;
|
||||
_documentServiceConnector = documentServiceConnector;
|
||||
_entryManager = entryManager;
|
||||
_fileConverter = fileConverter;
|
||||
}
|
||||
|
||||
|
||||
public void Deconstruct(out ILogger optionsMonitor,
|
||||
out TenantManager tenantManager,
|
||||
out UserManager userManager,
|
||||
out SecurityContext securityContext,
|
||||
out IDaoFactory daoFactory,
|
||||
out FileSecurity fileSecurity,
|
||||
out PathProvider pathProvider,
|
||||
out SetupInfo setupInfo,
|
||||
out FileUtility fileUtility,
|
||||
out DocumentServiceHelper documentServiceHelper,
|
||||
out DocumentServiceConnector documentServiceConnector,
|
||||
out EntryStatusManager entryManager,
|
||||
out FileConverter fileConverter)
|
||||
{
|
||||
optionsMonitor = _options;
|
||||
tenantManager = _tenantManager;
|
||||
userManager = _userManager;
|
||||
securityContext = _securityContext;
|
||||
daoFactory = _daoFactory;
|
||||
fileSecurity = _fileSecurity;
|
||||
pathProvider = _pathProvider;
|
||||
setupInfo = _setupInfo;
|
||||
fileUtility = _fileUtility;
|
||||
documentServiceHelper = _documentServiceHelper;
|
||||
documentServiceConnector = _documentServiceConnector;
|
||||
entryManager = _entryManager;
|
||||
fileConverter = _fileConverter;
|
||||
}
|
||||
|
||||
}
|
@ -25,7 +25,7 @@
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Files.Core.Log;
|
||||
internal static partial class FileConverterLogger
|
||||
public static partial class FileConverterLogger
|
||||
{
|
||||
[LoggerMessage(Level = LogLevel.Debug, Message = "Run CheckConvertFilesStatus: count {count}")]
|
||||
public static partial void DebugRunCheckConvertFilesStatus(this ILogger logger, int count);
|
@ -68,6 +68,13 @@ builder.Host.ConfigureDefault(args, (hostContext, config, env, path) =>
|
||||
|
||||
diHelper.TryAdd<FileDataQueue>();
|
||||
|
||||
// services.AddActivePassiveHostedService<FileConverterService<string>>();
|
||||
// diHelper.TryAdd<FileConverterService<string>>();
|
||||
|
||||
services.AddActivePassiveHostedService<FileConverterService<int>>();
|
||||
diHelper.TryAdd<FileConverterService<int>>();
|
||||
|
||||
|
||||
services.AddHostedService<ThumbnailBuilderService>();
|
||||
diHelper.TryAdd<ThumbnailBuilderService>();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user