Merge branch 'develop' into feature/integration-system

This commit is contained in:
Ilya Oleshko 2022-06-30 10:36:09 +03:00
commit 9d8a3c0104
35 changed files with 747 additions and 404 deletions

View File

@ -61,8 +61,8 @@ public class CustomEndpointDataSource : EndpointDataSource
void AddEndpoints(IReadOnlyDictionary<string, object> defaults = null, RouteValueDictionary policies = null) void AddEndpoints(IReadOnlyDictionary<string, object> defaults = null, RouteValueDictionary policies = null)
{ {
var order = constraintRouteAttr != null ? r.Order : r.Order + 2; var order = constraintRouteAttr != null ? r.Order : r.Order + 2;
endpoints.Add(new RouteEndpoint(r.RequestDelegate, RoutePatternFactory.Parse(r.RoutePattern.RawText, defaults, policies), order, r.Metadata, r.DisplayName)); endpoints.Add(new RouteEndpoint(r.RequestDelegate, RoutePatternFactory.Parse(r.RoutePattern.RawText, defaults, policies), order + 1, r.Metadata, r.DisplayName));
endpoints.Add(new RouteEndpoint(r.RequestDelegate, RoutePatternFactory.Parse(r.RoutePattern.RawText + ".{format}", defaults, policies), order - 1, r.Metadata, r.DisplayName)); endpoints.Add(new RouteEndpoint(r.RequestDelegate, RoutePatternFactory.Parse(r.RoutePattern.RawText + ".{format}", defaults, policies), order, r.Metadata, r.DisplayName));
} }
}).ToList(); }).ToList();

View File

@ -31,7 +31,7 @@ public class TempPath
{ {
private readonly string _tempFolder; private readonly string _tempFolder;
public TempPath(IConfiguration configuration) public TempPath(IHostEnvironment hostEnvironment, IConfiguration configuration)
{ {
var rootFolder = AppContext.BaseDirectory; var rootFolder = AppContext.BaseDirectory;
if (string.IsNullOrEmpty(rootFolder)) if (string.IsNullOrEmpty(rootFolder))
@ -39,7 +39,7 @@ public class TempPath
rootFolder = Assembly.GetEntryAssembly().Location; rootFolder = Assembly.GetEntryAssembly().Location;
} }
_tempFolder = configuration["temp"] ?? Path.Combine("..", "Data", "temp"); _tempFolder = configuration["web:temp"] ?? CrossPlatform.PathCombine(hostEnvironment.ContentRootPath, "temp");
if (!Path.IsPathRooted(_tempFolder)) if (!Path.IsPathRooted(_tempFolder))
{ {
_tempFolder = Path.GetFullPath(Path.Combine(rootFolder, _tempFolder)); _tempFolder = Path.GetFullPath(Path.Combine(rootFolder, _tempFolder));
@ -56,7 +56,7 @@ public class TempPath
return _tempFolder; return _tempFolder;
} }
public string GetTempFileName() public string GetTempFileName(string ext = "")
{ {
FileStream f = null; FileStream f = null;
string path; string path;
@ -66,6 +66,11 @@ public class TempPath
{ {
path = Path.Combine(_tempFolder, Path.GetRandomFileName()); path = Path.Combine(_tempFolder, Path.GetRandomFileName());
if (!string.IsNullOrEmpty(ext))
{
path = Path.ChangeExtension(path, ext);
}
try try
{ {
using (f = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read)) using (f = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read))

View File

@ -54,7 +54,12 @@ public class TempStream
public Stream Create() public Stream Create()
{ {
return new FileStream(_tempPath.GetTempFileName(), FileMode.OpenOrCreate, return Create(out _);
FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose); }
public Stream Create(out string path, string ext = "")
{
path = _tempPath.GetTempFileName(ext);
return new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose);
} }
} }

View File

@ -86,6 +86,7 @@ global using Microsoft.Extensions.Caching.Memory;
global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.Extensions.DependencyInjection.Extensions;
global using Microsoft.Extensions.Hosting;
global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Options; global using Microsoft.Extensions.Options;
global using Microsoft.Extensions.Primitives; global using Microsoft.Extensions.Primitives;

View File

@ -1,53 +1,53 @@
// (c) Copyright Ascensio System SIA 2010-2022 // (c) Copyright Ascensio System SIA 2010-2022
// //
// This program is a free software product. // This program is a free software product.
// You can redistribute it and/or modify it under the terms // 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 // 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 // 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 // to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights. // any third-party rights.
// //
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty // This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html // 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. // 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 // 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. // 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 // 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 // distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks. // trademark law for use of our trademarks.
// //
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing // 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 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Files.Core; namespace ASC.Files.Core;
[Scope] [Scope]
public interface IFileDao<T> public interface IFileDao<T>
{ {
/// <summary> /// <summary>
/// Clear the application cache for the specific file /// Clear the application cache for the specific file
/// </summary> /// </summary>
Task InvalidateCacheAsync(T fileId); Task InvalidateCacheAsync(T fileId);
/// <summary> /// <summary>
/// Receive file /// Receive file
/// </summary> /// </summary>
/// <param name="fileId">file id</param> /// <param name="fileId">file id</param>
/// <returns></returns> /// <returns></returns>
Task<File<T>> GetFileAsync(T fileId); Task<File<T>> GetFileAsync(T fileId);
/// <summary> /// <summary>
/// Receive file /// Receive file
/// </summary> /// </summary>
/// <param name="fileId">file id</param> /// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param> /// <param name="fileVersion">file version</param>
/// <returns></returns> /// <returns></returns>
Task<File<T>> GetFileAsync(T fileId, int fileVersion); Task<File<T>> GetFileAsync(T fileId, int fileVersion);
/// <summary> /// <summary>
/// Receive file /// Receive file
/// </summary> /// </summary>
@ -55,29 +55,29 @@ public interface IFileDao<T>
/// <param name="title">file name</param> /// <param name="title">file name</param>
/// <returns> /// <returns>
/// file /// file
/// </returns> /// </returns>
Task<File<T>> GetFileAsync(T parentId, string title); Task<File<T>> GetFileAsync(T parentId, string title);
/// <summary> /// <summary>
/// Receive last file without forcesave /// Receive last file without forcesave
/// </summary> /// </summary>
/// <param name="fileId">file id</param> /// <param name="fileId">file id</param>
/// <param name="fileVersion"></param> /// <param name="fileVersion"></param>
/// <returns></returns> /// <returns></returns>
Task<File<T>> GetFileStableAsync(T fileId, int fileVersion = -1); Task<File<T>> GetFileStableAsync(T fileId, int fileVersion = -1);
/// <summary> /// <summary>
/// Returns all versions of the file /// Returns all versions of the file
/// </summary> /// </summary>
/// <param name="fileId"></param> /// <param name="fileId"></param>
/// <returns></returns> /// <returns></returns>
IAsyncEnumerable<File<T>> GetFileHistoryAsync(T fileId); IAsyncEnumerable<File<T>> GetFileHistoryAsync(T fileId);
/// <summary> /// <summary>
/// Gets the file (s) by ID (s) /// Gets the file (s) by ID (s)
/// </summary> /// </summary>
/// <param name="fileIds">id file</param> /// <param name="fileIds">id file</param>
/// <returns></returns> /// <returns></returns>
IAsyncEnumerable<File<T>> GetFilesAsync(IEnumerable<T> fileIds); IAsyncEnumerable<File<T>> GetFilesAsync(IEnumerable<T> fileIds);
/// <summary> /// <summary>
/// Gets the file (s) by ID (s) for share /// Gets the file (s) by ID (s) for share
/// </summary> /// </summary>
@ -88,15 +88,15 @@ public interface IFileDao<T>
/// <param name="searchText"></param> /// <param name="searchText"></param>
/// <param name="searchInContent"></param> /// <param name="searchInContent"></param>
/// <returns></returns> /// <returns></returns>
IAsyncEnumerable<File<T>> GetFilesFilteredAsync(IEnumerable<T> fileIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool checkShared = false); IAsyncEnumerable<File<T>> GetFilesFilteredAsync(IEnumerable<T> fileIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool checkShared = false);
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="parentId"></param> /// <param name="parentId"></param>
/// <returns></returns> /// <returns></returns>
Task<List<T>> GetFilesAsync(T parentId); Task<List<T>> GetFilesAsync(T parentId);
/// <summary> /// <summary>
/// Get files in folder /// Get files in folder
/// </summary> /// </summary>
@ -111,39 +111,39 @@ public interface IFileDao<T>
/// <returns>list of files</returns> /// <returns>list of files</returns>
/// <remarks> /// <remarks>
/// Return only the latest versions of files of a folder /// Return only the latest versions of files of a folder
/// </remarks> /// </remarks>
IAsyncEnumerable<File<T>> GetFilesAsync(T parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false); IAsyncEnumerable<File<T>> GetFilesAsync(T parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false);
/// <summary> /// <summary>
/// Get stream of file /// Get stream of file
/// </summary> /// </summary>
/// <param name="file"></param> /// <param name="file"></param>
/// <returns>Stream</returns> /// <returns>Stream</returns>
Task<Stream> GetFileStreamAsync(File<T> file); Task<Stream> GetFileStreamAsync(File<T> file);
/// <summary> /// <summary>
/// Get stream of file /// Get stream of file
/// </summary> /// </summary>
/// <param name="file"></param> /// <param name="file"></param>
/// <param name="offset"></param> /// <param name="offset"></param>
/// <returns>Stream</returns> /// <returns>Stream</returns>
Task<Stream> GetFileStreamAsync(File<T> file, long offset); Task<Stream> GetFileStreamAsync(File<T> file, long offset);
/// <summary> /// <summary>
/// Get presigned uri /// Get presigned uri
/// </summary> /// </summary>
/// <param name="file"></param> /// <param name="file"></param>
/// <param name="expires"></param> /// <param name="expires"></param>
/// <returns>Stream uri</returns> /// <returns>Stream uri</returns>
Task<Uri> GetPreSignedUriAsync(File<T> file, TimeSpan expires); Task<Uri> GetPreSignedUriAsync(File<T> file, TimeSpan expires);
/// <summary> /// <summary>
/// Check is supported PreSignedUri /// Check is supported PreSignedUri
/// </summary> /// </summary>
/// <param name="file"></param> /// <param name="file"></param>
/// <returns>Stream uri</returns> /// <returns>Stream uri</returns>
Task<bool> IsSupportedPreSignedUriAsync(File<T> file); Task<bool> IsSupportedPreSignedUriAsync(File<T> file);
/// <summary> /// <summary>
/// Saves / updates the version of the file /// Saves / updates the version of the file
/// and save stream of file /// and save stream of file
@ -158,96 +158,96 @@ public interface IFileDao<T>
/// ///
/// Save in all other cases /// Save in all other cases
/// </remarks> /// </remarks>
Task<File<T>> SaveFileAsync(File<T> file, Stream fileStream); Task<File<T>> SaveFileAsync(File<T> file, Stream fileStream);
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="file"></param> /// <param name="file"></param>
/// <param name="fileStream"></param> /// <param name="fileStream"></param>
/// <returns></returns> /// <returns></returns>
Task<File<T>> ReplaceFileVersionAsync(File<T> file, Stream fileStream); Task<File<T>> ReplaceFileVersionAsync(File<T> file, Stream fileStream);
/// <summary> /// <summary>
/// Deletes a file including all previous versions /// Deletes a file including all previous versions
/// </summary> /// </summary>
/// <param name="fileId">file id</param> /// <param name="fileId">file id</param>
Task DeleteFileAsync(T fileId); Task DeleteFileAsync(T fileId);
/// <summary> /// <summary>
/// Checks whether or not file /// Checks whether or not file
/// </summary> /// </summary>
/// <param name="title">file name</param> /// <param name="title">file name</param>
/// <param name="folderId">folder id</param> /// <param name="folderId">folder id</param>
/// <returns>Returns true if the file exists, otherwise false</returns> /// <returns>Returns true if the file exists, otherwise false</returns>
Task<bool> IsExistAsync(string title, object folderId); Task<bool> IsExistAsync(string title, object folderId);
/// <summary> /// <summary>
/// Moves a file or set of files in a folder /// Moves a file or set of files in a folder
/// </summary> /// </summary>
/// <param name="fileId">file id</param> /// <param name="fileId">file id</param>
/// <param name="toFolderId">The ID of the destination folder</param> /// <param name="toFolderId">The ID of the destination folder</param>
Task<T> MoveFileAsync(T fileId, T toFolderId); Task<T> MoveFileAsync(T fileId, T toFolderId);
Task<TTo> MoveFileAsync<TTo>(T fileId, TTo toFolderId); Task<TTo> MoveFileAsync<TTo>(T fileId, TTo toFolderId);
Task<string> MoveFileAsync(T fileId, string toFolderId); Task<string> MoveFileAsync(T fileId, string toFolderId);
Task<int> MoveFileAsync(T fileId, int toFolderId); Task<int> MoveFileAsync(T fileId, int toFolderId);
/// <summary> /// <summary>
/// Copy the files in a folder /// Copy the files in a folder
/// </summary> /// </summary>
/// <param name="fileId">file id</param> /// <param name="fileId">file id</param>
/// <param name="toFolderId">The ID of the destination folder</param> /// <param name="toFolderId">The ID of the destination folder</param>
Task<File<T>> CopyFileAsync(T fileId, T toFolderId); Task<File<T>> CopyFileAsync(T fileId, T toFolderId);
Task<File<TTo>> CopyFileAsync<TTo>(T fileId, TTo toFolderId); Task<File<TTo>> CopyFileAsync<TTo>(T fileId, TTo toFolderId);
Task<File<string>> CopyFileAsync(T fileId, string toFolderId); Task<File<string>> CopyFileAsync(T fileId, string toFolderId);
Task<File<int>> CopyFileAsync(T fileId, int toFolderId); Task<File<int>> CopyFileAsync(T fileId, int toFolderId);
/// <summary> /// <summary>
/// Rename file /// Rename file
/// </summary> /// </summary>
/// <param name="file"></param> /// <param name="file"></param>
/// <param name="newTitle">new name</param> /// <param name="newTitle">new name</param>
Task<T> FileRenameAsync(File<T> file, string newTitle); Task<T> FileRenameAsync(File<T> file, string newTitle);
/// <summary> /// <summary>
/// Update comment file /// Update comment file
/// </summary> /// </summary>
/// <param name="fileId">file id</param> /// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param> /// <param name="fileVersion">file version</param>
/// <param name="comment">new comment</param> /// <param name="comment">new comment</param>
Task<string> UpdateCommentAsync(T fileId, int fileVersion, string comment); Task<string> UpdateCommentAsync(T fileId, int fileVersion, string comment);
/// <summary> /// <summary>
/// Complete file version /// Complete file version
/// </summary> /// </summary>
/// <param name="fileId">file id</param> /// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param> /// <param name="fileVersion">file version</param>
Task CompleteVersionAsync(T fileId, int fileVersion); Task CompleteVersionAsync(T fileId, int fileVersion);
/// <summary> /// <summary>
/// Continue file version /// Continue file version
/// </summary> /// </summary>
/// <param name="fileId">file id</param> /// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param> /// <param name="fileVersion">file version</param>
Task ContinueVersionAsync(T fileId, int fileVersion); Task ContinueVersionAsync(T fileId, int fileVersion);
/// <summary> /// <summary>
/// Check the need to use the trash before removing /// Check the need to use the trash before removing
/// </summary> /// </summary>
/// <param name="file"></param> /// <param name="file"></param>
/// <returns></returns> /// <returns></returns>
bool UseTrashForRemove(File<T> file); bool UseTrashForRemove(File<T> file);
string GetUniqFilePath(File<T> file, string fileTitle); string GetUniqFilePath(File<T> file, string fileTitle);
#region chunking #region chunking
Task<ChunkedUploadSession<T>> CreateUploadSessionAsync(File<T> file, long contentLength); Task<ChunkedUploadSession<T>> CreateUploadSessionAsync(File<T> file, long contentLength);
Task<File<T>> UploadChunkAsync(ChunkedUploadSession<T> uploadSession, Stream chunkStream, long chunkLength); Task<File<T>> UploadChunkAsync(ChunkedUploadSession<T> uploadSession, Stream chunkStream, long chunkLength);
Task AbortUploadSessionAsync(ChunkedUploadSession<T> uploadSession); Task AbortUploadSessionAsync(ChunkedUploadSession<T> uploadSession);
#endregion #endregion
#region Only in TMFileDao #region Only in TMFileDao
/// <summary> /// <summary>
/// Set created by /// Set created by
/// </summary> /// </summary>
/// <param name="fileIds"></param> /// <param name="fileIds"></param>
/// <param name="newOwnerId"></param> /// <param name="newOwnerId"></param>
Task ReassignFilesAsync(T[] fileIds, Guid newOwnerId); Task ReassignFilesAsync(T[] fileIds, Guid newOwnerId);
/// <summary> /// <summary>
/// Search files in SharedWithMe & Projects /// Search files in SharedWithMe & Projects
/// </summary> /// </summary>
@ -258,7 +258,7 @@ public interface IFileDao<T>
/// <param name="searchText"></param> /// <param name="searchText"></param>
/// <param name="searchInContent"></param> /// <param name="searchInContent"></param>
/// <returns></returns> /// <returns></returns>
Task<List<File<T>>> GetFilesAsync(IEnumerable<T> parentIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent); Task<List<File<T>>> GetFilesAsync(IEnumerable<T> parentIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent);
/// <summary> /// <summary>
/// Search the list of files containing text /// Search the list of files containing text
/// Only in TMFileDao /// Only in TMFileDao
@ -266,34 +266,36 @@ public interface IFileDao<T>
/// <param name="text">search text</param> /// <param name="text">search text</param>
/// <param name="bunch"></param> /// <param name="bunch"></param>
/// <returns>list of files</returns> /// <returns>list of files</returns>
IAsyncEnumerable<File<T>> SearchAsync(string text, bool bunch = false); IAsyncEnumerable<File<T>> SearchAsync(string text, bool bunch = false);
/// <summary> /// <summary>
/// Checks whether file exists on storage /// Checks whether file exists on storage
/// </summary> /// </summary>
/// <param name="file">file</param> /// <param name="file">file</param>
/// <returns></returns> /// <returns></returns>
Task<bool> IsExistOnStorageAsync(File<T> file); Task<bool> IsExistOnStorageAsync(File<T> file);
Task SaveEditHistoryAsync(File<T> file, string changes, Stream differenceStream); Task SaveEditHistoryAsync(File<T> file, string changes, Stream differenceStream);
Task<List<EditHistory>> GetEditHistoryAsync(DocumentServiceHelper documentServiceHelper, T fileId, int fileVersion = 0); Task<List<EditHistory>> GetEditHistoryAsync(DocumentServiceHelper documentServiceHelper, T fileId, int fileVersion = 0);
Task<Stream> GetDifferenceStreamAsync(File<T> file); Task<Stream> GetDifferenceStreamAsync(File<T> file);
Task<bool> ContainChangesAsync(T fileId, int fileVersion); Task<bool> ContainChangesAsync(T fileId, int fileVersion);
Task SaveThumbnailAsync(File<T> file, Stream thumbnail, int width, int height); Task SaveThumbnailAsync(File<T> file, Stream thumbnail, int width, int height);
Task<Stream> GetThumbnailAsync(File<T> file, int width, int height); Task<Stream> GetThumbnailAsync(File<T> file, int width, int height);
Task<IEnumerable<FileWithShare>> GetFeedsAsync(int tenant, DateTime from, DateTime to); Task<Stream> GetThumbnailAsync(T fileId, int width, int height);
Task<IEnumerable<int>> GetTenantsWithFeedsAsync(DateTime fromTime); Task<IEnumerable<FileWithShare>> GetFeedsAsync(int tenant, DateTime from, DateTime to);
Task<EntryProperties> GetProperties(T fileId); Task<IEnumerable<int>> GetTenantsWithFeedsAsync(DateTime fromTime);
Task SaveProperties(T fileId, EntryProperties entryProperties); Task<EntryProperties> GetProperties(T fileId);
Task SaveProperties(T fileId, EntryProperties entryProperties);
#endregion #endregion
} }

View File

@ -1465,6 +1465,12 @@ internal class FileDao : AbstractDao, IFileDao<int>
await _globalStore.GetStore().SaveAsync(string.Empty, GetUniqFilePath(file, thumnailName), thumbnail, thumnailName); await _globalStore.GetStore().SaveAsync(string.Empty, GetUniqFilePath(file, thumnailName), thumbnail, thumnailName);
} }
public async Task<Stream> GetThumbnailAsync(int fileId, int width, int height)
{
var file = await GetFileAsync(fileId);
return await GetThumbnailAsync(file, width, height);
}
public async Task<Stream> GetThumbnailAsync(File<int> file, int width, int height) public async Task<Stream> GetThumbnailAsync(File<int> file, int width, int height)
{ {
var thumnailName = GetThumnailName(width, height); var thumnailName = GetThumnailName(width, height);

View File

@ -192,6 +192,7 @@ internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>
file.ModifiedOn = boxFile.ModifiedAt.HasValue ? _tenantUtil.DateTimeFromUtc(boxFile.ModifiedAt.Value.UtcDateTime) : default; file.ModifiedOn = boxFile.ModifiedAt.HasValue ? _tenantUtil.DateTimeFromUtc(boxFile.ModifiedAt.Value.UtcDateTime) : default;
file.NativeAccessor = boxFile; file.NativeAccessor = boxFile;
file.Title = MakeFileTitle(boxFile); file.Title = MakeFileTitle(boxFile);
file.ThumbnailStatus = Thumbnail.Created;
return file; return file;
} }

View File

@ -550,6 +550,12 @@ internal class BoxFileDao : BoxDaoBase, IFileDao<string>
return false; return false;
} }
public override Task<Stream> GetThumbnailAsync(string fileId, int width, int height)
{
var boxFileId = MakeBoxId(_boxDaoSelector.ConvertId(fileId));
return ProviderInfo.GetThumbnailAsync(boxFileId, width, height);
}
#region chunking #region chunking
private File<string> RestoreIds(File<string> file) private File<string> RestoreIds(File<string> file)

View File

@ -147,6 +147,12 @@ internal class BoxProviderInfo : IProviderInfo
{ {
return _boxProviderInfoHelper.CacheResetAsync(BoxRootId, ID, boxPath, isFile); return _boxProviderInfoHelper.CacheResetAsync(BoxRootId, ID, boxPath, isFile);
} }
internal async Task<Stream> GetThumbnailAsync(string boxFileId, int width, int height)
{
var storage = await StorageAsync;
return await _boxProviderInfoHelper.GetThumbnailAsync(storage, boxFileId, width, height);
}
} }
[Scope] [Scope]
@ -332,4 +338,9 @@ public class BoxProviderInfoHelper
await _cacheNotify.PublishAsync(new BoxCacheItem { IsFile = isFile ?? false, IsFileExists = isFile.HasValue, Key = key }, CacheNotifyAction.Remove).ConfigureAwait(false); await _cacheNotify.PublishAsync(new BoxCacheItem { IsFile = isFile ?? false, IsFileExists = isFile.HasValue, Key = key }, CacheNotifyAction.Remove).ConfigureAwait(false);
} }
} }
internal async Task<Stream> GetThumbnailAsync(BoxStorage storage, string boxFileId, int width, int height)
{
return await storage.GetThumbnailAsync(boxFileId, width, height).ConfigureAwait(false);
}
} }

View File

@ -270,4 +270,13 @@ internal class BoxStorage
//todo: without chunked uploader: //todo: without chunked uploader:
return Math.Min(max, MaxChunkedUploadFileSize); return Math.Min(max, MaxChunkedUploadFileSize);
} }
public async Task<Stream> GetThumbnailAsync(string fileId, int width, int height)
{
var boxRepresentation = new BoxRepresentationRequest();
boxRepresentation.FileId = fileId;
boxRepresentation.XRepHints = $"[jpg?dimensions=320x320]";
return await _boxClient.FilesManager.GetRepresentationContentAsync(boxRepresentation);
}
} }

View File

@ -196,6 +196,7 @@ internal abstract class DropboxDaoBase : ThirdPartyProviderDao<DropboxProviderIn
file.ModifiedOn = _tenantUtil.DateTimeFromUtc(dropboxFile.ServerModified); file.ModifiedOn = _tenantUtil.DateTimeFromUtc(dropboxFile.ServerModified);
file.NativeAccessor = dropboxFile; file.NativeAccessor = dropboxFile;
file.Title = MakeFileTitle(dropboxFile); file.Title = MakeFileTitle(dropboxFile);
file.ThumbnailStatus = Thumbnail.Created;
return file; return file;
} }

View File

@ -185,6 +185,7 @@ internal class DropboxFileDao : DropboxDaoBase, IFileDao<string>
} }
//Get only files //Get only files
var items = await GetDropboxItemsAsync(parentId, false).ConfigureAwait(false); var items = await GetDropboxItemsAsync(parentId, false).ConfigureAwait(false);
var files = items.Select(item => ToFile(item.AsFile)); var files = items.Select(item => ToFile(item.AsFile));
@ -548,6 +549,11 @@ internal class DropboxFileDao : DropboxDaoBase, IFileDao<string>
return false; return false;
} }
public override Task<Stream> GetThumbnailAsync(string fileId, int width, int height)
{
return ProviderInfo.GetThumbnailsAsync(_dropboxDaoSelector.ConvertId(fileId), width, height);
}
#region chunking #region chunking
private File<string> RestoreIds(File<string> file) private File<string> RestoreIds(File<string> file)

View File

@ -132,9 +132,15 @@ internal class DropboxProviderInfo : IProviderInfo
{ {
return _dropboxProviderInfoHelper.CacheResetAsync(ID, dropboxPath, isFile); return _dropboxProviderInfoHelper.CacheResetAsync(ID, dropboxPath, isFile);
} }
internal async Task<Stream> GetThumbnailsAsync(string filePath, int width, int height)
{
var storage = await StorageAsync;
return await _dropboxProviderInfoHelper.GetThumbnailsAsync(storage, filePath, width, height);
}
} }
[Scope] [Scope(Additional = typeof(DropboxStorageDisposableWrapperExtention))]
internal class DropboxStorageDisposableWrapper : IDisposable internal class DropboxStorageDisposableWrapper : IDisposable
{ {
public DropboxStorage Storage { get; private set; } public DropboxStorage Storage { get; private set; }
@ -163,7 +169,7 @@ internal class DropboxStorageDisposableWrapper : IDisposable
public async Task<DropboxStorage> InternalCreateStorageAsync(OAuth20Token token, int id) public async Task<DropboxStorage> InternalCreateStorageAsync(OAuth20Token token, int id)
{ {
var dropboxStorage = _serviceProvider.GetService<DropboxStorage>(); var dropboxStorage = _serviceProvider.GetRequiredService<DropboxStorage>();
await CheckTokenAsync(token, id).ConfigureAwait(false); await CheckTokenAsync(token, id).ConfigureAwait(false);
@ -311,4 +317,17 @@ public class DropboxProviderInfoHelper
await _cacheNotify.PublishAsync(new DropboxCacheItem { IsFile = isFile ?? false, IsFileExists = isFile.HasValue, Key = key }, CacheNotifyAction.Remove).ConfigureAwait(false); await _cacheNotify.PublishAsync(new DropboxCacheItem { IsFile = isFile ?? false, IsFileExists = isFile.HasValue, Key = key }, CacheNotifyAction.Remove).ConfigureAwait(false);
} }
} }
internal Task<Stream> GetThumbnailsAsync(DropboxStorage storage, string filePath, int width, int height)
{
return storage.GetThumbnailsAsync(filePath, width, height);
}
} }
public static class DropboxStorageDisposableWrapperExtention
{
public static void Register(DIHelper dIHelper)
{
dIHelper.TryAdd<DropboxStorage>();
}
}

View File

@ -24,8 +24,11 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using ThumbnailSize = Dropbox.Api.Files.ThumbnailSize;
namespace ASC.Files.Thirdparty.Dropbox; namespace ASC.Files.Thirdparty.Dropbox;
[Scope]
internal class DropboxStorage : IDisposable internal class DropboxStorage : IDisposable
{ {
public bool IsOpened { get; private set; } public bool IsOpened { get; private set; }
@ -135,10 +138,38 @@ internal class DropboxStorage : IDisposable
public async Task<List<Metadata>> GetItemsAsync(string folderPath) public async Task<List<Metadata>> GetItemsAsync(string folderPath)
{ {
var data = await _dropboxClient.Files.ListFolderAsync(folderPath); var data = await _dropboxClient.Files.ListFolderAsync(folderPath);
return new List<Metadata>(data.Entries); return new List<Metadata>(data.Entries);
} }
public async Task<Stream> GetThumbnailsAsync(string filePath, int width, int height)
{
try
{
var path = new PathOrLink.Path(filePath);
var size = Convert(width, height);
var arg = new ThumbnailV2Arg(path, size: size);
var responce = await _dropboxClient.Files.GetThumbnailV2Async(arg);
return await responce.GetContentAsStreamAsync();
}
catch
{
return null;
}
}
private ThumbnailSize Convert(int width, int height)
{
if (width > 368)
{
return ThumbnailSize.W480h320.Instance;
}
else
{
return ThumbnailSize.W256h256.Instance;
}
}
public Task<Stream> DownloadStreamAsync(string filePath, int offset = 0) public Task<Stream> DownloadStreamAsync(string filePath, int offset = 0)
{ {
ArgumentNullOrEmptyException.ThrowIfNullOrEmpty(filePath); ArgumentNullOrEmptyException.ThrowIfNullOrEmpty(filePath);

View File

@ -215,6 +215,7 @@ internal abstract class GoogleDriveDaoBase : ThirdPartyProviderDao<GoogleDrivePr
file.ModifiedOn = driveFile.ModifiedTime.HasValue ? _tenantUtil.DateTimeFromUtc(driveFile.ModifiedTime.Value) : default; file.ModifiedOn = driveFile.ModifiedTime.HasValue ? _tenantUtil.DateTimeFromUtc(driveFile.ModifiedTime.Value) : default;
file.NativeAccessor = driveFile; file.NativeAccessor = driveFile;
file.Title = MakeFileTitle(driveFile); file.Title = MakeFileTitle(driveFile);
file.ThumbnailStatus = Thumbnail.Created;
return file; return file;
} }

View File

@ -552,6 +552,12 @@ internal class GoogleDriveFileDao : GoogleDriveDaoBase, IFileDao<string>
return false; return false;
} }
public override Task<Stream> GetThumbnailAsync(string fileId, int width, int height)
{
return ProviderInfo.GetThumbnail(MakeDriveId(_googleDriveDaoSelector.ConvertId(fileId)), width, height);
}
#region chunking #region chunking
private File<string> RestoreIds(File<string> file) private File<string> RestoreIds(File<string> file)
@ -706,5 +712,6 @@ internal class GoogleDriveFileDao : GoogleDriveDaoBase, IFileDao<string>
return Task.CompletedTask; return Task.CompletedTask;
} }
#endregion #endregion
} }

View File

@ -158,6 +158,12 @@ internal class GoogleDriveProviderInfo : IProviderInfo
{ {
return _googleDriveProviderInfoHelper.CacheResetChildsAsync(ID, parentDriveId, childFolder); return _googleDriveProviderInfoHelper.CacheResetChildsAsync(ID, parentDriveId, childFolder);
} }
internal async Task<Stream> GetThumbnail(string fileId, int width, int height)
{
var storage = await StorageAsync;
return await storage.GetThumbnail(fileId, width, height);
}
} }
[Scope(Additional = typeof(GoogleDriveProviderInfoExtention))] [Scope(Additional = typeof(GoogleDriveProviderInfoExtention))]

View File

@ -155,7 +155,6 @@ internal class GoogleDriveStorage : IDisposable
try try
{ {
var request = _driveService.Files.Get(entryId); var request = _driveService.Files.Get(entryId);
request.Fields = GoogleLoginProvider.FilesFields; request.Fields = GoogleLoginProvider.FilesFields;
return await request.ExecuteAsync(); return await request.ExecuteAsync();
@ -170,6 +169,21 @@ internal class GoogleDriveStorage : IDisposable
} }
} }
public async Task<Stream> GetThumbnail(string fileId, int width, int height)
{
try
{
var url = $"https://lh3.google.com/u/0/d/{fileId}=w{width}-h{height}-p-k-nu-iv1";
var httpClient = _driveService.HttpClient;
var response = await httpClient.GetAsync(url);
return await response.Content.ReadAsStreamAsync();
}
catch (Exception)
{
return null;
}
}
public List<DriveFile> GetEntries(string folderId, bool? folders = null) public List<DriveFile> GetEntries(string folderId, bool? folders = null)
{ {
var request = _driveService.Files.List(); var request = _driveService.Files.List();

View File

@ -71,13 +71,18 @@ internal abstract class ThirdPartyProviderDao
return Task.FromResult(false); return Task.FromResult(false);
} }
public Task SaveThumbnailAsync(File<string> file, Stream thumbnail, int width, int height) public Task SaveThumbnailAsync(File<string> file, Stream thumbnail, int width, int height)
{ {
//Do nothing //Do nothing
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task<Stream> GetThumbnailAsync(File<string> file, int width, int height) public virtual Task<Stream> GetThumbnailAsync(File<string> file, int width, int height)
{
return GetThumbnailAsync(file.Id, width, height);
}
public virtual Task<Stream> GetThumbnailAsync(string file, int width, int height)
{ {
return Task.FromResult<Stream>(null); return Task.FromResult<Stream>(null);
} }
@ -96,7 +101,7 @@ internal abstract class ThirdPartyProviderDao
{ {
return null; return null;
} }
public string GetUniqFilePath(File<string> file, string fileTitle) public string GetUniqFilePath(File<string> file, string fileTitle)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@ -177,7 +182,7 @@ internal abstract class ThirdPartyProviderDao
{ {
return null; return null;
} }
public string GetFolderIDPhotos(bool createIfNotExists) public string GetFolderIDPhotos(bool createIfNotExists)
{ {
return null; return null;
@ -240,9 +245,9 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
protected List<FilterType> _constraintFolderFilters = protected List<FilterType> _constraintFolderFilters =
new List<FilterType> { FilterType.FilesOnly, FilterType.ByExtension, FilterType.DocumentsOnly, FilterType.ImagesOnly, new List<FilterType> { FilterType.FilesOnly, FilterType.ByExtension, FilterType.DocumentsOnly, FilterType.ImagesOnly,
FilterType.PresentationsOnly, FilterType.SpreadsheetsOnly, FilterType.ArchiveOnly, FilterType.MediaOnly }; FilterType.PresentationsOnly, FilterType.SpreadsheetsOnly, FilterType.ArchiveOnly, FilterType.MediaOnly };
protected abstract string Id { get; } protected abstract string Id { get; }
protected ThirdPartyProviderDao( protected ThirdPartyProviderDao(
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
UserManager userManager, UserManager userManager,
@ -264,14 +269,14 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
_tempPath = tempPath; _tempPath = tempPath;
TenantID = tenantManager.GetCurrentTenant().Id; TenantID = tenantManager.GetCurrentTenant().Id;
} }
public void Init(BaseProviderInfo<T> providerInfo, RegexDaoSelectorBase<T> selectorBase) public void Init(BaseProviderInfo<T> providerInfo, RegexDaoSelectorBase<T> selectorBase)
{ {
ProviderInfo = providerInfo.ProviderInfo; ProviderInfo = providerInfo.ProviderInfo;
PathPrefix = providerInfo.PathPrefix; PathPrefix = providerInfo.PathPrefix;
DaoSelector = selectorBase; DaoSelector = selectorBase;
} }
protected IQueryable<TSet> Query<TSet>(DbSet<TSet> set) where TSet : class, IDbFile protected IQueryable<TSet> Query<TSet>(DbSet<TSet> set) where TSet : class, IDbFile
{ {
return set.AsQueryable().Where(r => r.TenantId == TenantID); return set.AsQueryable().Where(r => r.TenantId == TenantID);
@ -283,7 +288,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
{ {
return null; return null;
} }
return InternalMappingIDAsync(id, saveIfNotExist); return InternalMappingIDAsync(id, saveIfNotExist);
} }
@ -311,52 +316,52 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
HashId = result, HashId = result,
TenantId = TenantID TenantId = TenantID
}; };
await FilesDbContext.ThirdpartyIdMapping.AddAsync(newMapping).ConfigureAwait(false); await FilesDbContext.ThirdpartyIdMapping.AddAsync(newMapping).ConfigureAwait(false);
await FilesDbContext.SaveChangesAsync().ConfigureAwait(false); await FilesDbContext.SaveChangesAsync().ConfigureAwait(false);
} }
return result; return result;
} }
protected Folder<string> GetFolder() protected Folder<string> GetFolder()
{ {
var folder = _serviceProvider.GetService<Folder<string>>(); var folder = _serviceProvider.GetService<Folder<string>>();
InitFileEntry(folder); InitFileEntry(folder);
folder.FolderType = FolderType.DEFAULT; folder.FolderType = FolderType.DEFAULT;
folder.Shareable = false; folder.Shareable = false;
folder.FilesCount = 0; folder.FilesCount = 0;
folder.FoldersCount = 0; folder.FoldersCount = 0;
return folder; return folder;
} }
protected Folder<string> GetErrorFolder(ErrorEntry entry) protected Folder<string> GetErrorFolder(ErrorEntry entry)
{ {
var folder = GetFolder(); var folder = GetFolder();
InitFileEntryError(folder, entry); InitFileEntryError(folder, entry);
folder.ParentId = null; folder.ParentId = null;
return folder; return folder;
} }
protected File<string> GetFile() protected File<string> GetFile()
{ {
var file = _serviceProvider.GetService<File<string>>(); var file = _serviceProvider.GetService<File<string>>();
InitFileEntry(file); InitFileEntry(file);
file.Access = FileShare.None; file.Access = FileShare.None;
file.Shared = false; file.Shared = false;
file.Version = 1; file.Version = 1;
return file; return file;
} }
protected File<string> GetErrorFile(ErrorEntry entry) protected File<string> GetErrorFile(ErrorEntry entry)
{ {
var file = GetFile(); var file = GetFile();
@ -364,7 +369,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
return file; return file;
} }
protected void InitFileEntry(FileEntry<string> fileEntry) protected void InitFileEntry(FileEntry<string> fileEntry)
{ {
fileEntry.CreateBy = ProviderInfo.Owner; fileEntry.CreateBy = ProviderInfo.Owner;
@ -375,7 +380,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
fileEntry.RootFolderType = ProviderInfo.RootFolderType; fileEntry.RootFolderType = ProviderInfo.RootFolderType;
fileEntry.RootId = MakeId(); fileEntry.RootId = MakeId();
} }
protected void InitFileEntryError(FileEntry<string> fileEntry, ErrorEntry entry) protected void InitFileEntryError(FileEntry<string> fileEntry, ErrorEntry entry)
{ {
fileEntry.Id = MakeId(entry.ErrorId); fileEntry.Id = MakeId(entry.ErrorId);
@ -406,7 +411,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
var filtered = folders.Join(FilesDbContext.ThirdpartyIdMapping.ToAsyncEnumerable(), f => f.Id, m => m.Id, (folder, map) => new { folder, map.HashId }) var filtered = folders.Join(FilesDbContext.ThirdpartyIdMapping.ToAsyncEnumerable(), f => f.Id, m => m.Id, (folder, map) => new { folder, map.HashId })
.Join(FilesDbContext.TagLink.ToAsyncEnumerable(), r => r.HashId, t => t.EntryId, (result, tag) => new { result.folder, tag.TagId }) .Join(FilesDbContext.TagLink.ToAsyncEnumerable(), r => r.HashId, t => t.EntryId, (result, tag) => new { result.folder, tag.TagId })
.Join(FilesDbContext.Tag.ToAsyncEnumerable(), r => r.TagId, t => t.Id, (result, tagInfo) => new {result.folder, tagInfo.Name }) .Join(FilesDbContext.Tag.ToAsyncEnumerable(), r => r.TagId, t => t.Id, (result, tagInfo) => new { result.folder, tagInfo.Name })
.Where(r => tagNames.Contains(r.Name)) .Where(r => tagNames.Contains(r.Name))
.Select(r => r.folder); .Select(r => r.folder);
@ -449,10 +454,10 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task<List<FileShareRecord>> GetSharesAsync(IEnumerable<Guid> subjects) public Task<List<FileShareRecord>> GetSharesAsync(IEnumerable<Guid> subjects)
{ {
List<FileShareRecord> result = null; List<FileShareRecord> result = null;
return Task<List<FileShareRecord>>.FromResult(result); return Task<List<FileShareRecord>>.FromResult(result);
} }
public Task<IEnumerable<FileShareRecord>> GetSharesAsync(IEnumerable<FileEntry<string>> entry) public Task<IEnumerable<FileShareRecord>> GetSharesAsync(IEnumerable<FileEntry<string>> entry)
@ -474,7 +479,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
{ {
return null; return null;
} }
public Task<IEnumerable<FileShareRecord>> GetPureShareRecordsAsync(FileEntry<string> entry) public Task<IEnumerable<FileShareRecord>> GetPureShareRecordsAsync(FileEntry<string> entry)
{ {
return null; return null;
@ -553,16 +558,16 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
{ {
return new List<Tag>(); return new List<Tag>();
} }
public IEnumerable<Tag> SaveTags(Tag tag) public IEnumerable<Tag> SaveTags(Tag tag)
{ {
return new List<Tag>(); return new List<Tag>();
} }
public void UpdateNewTags(IEnumerable<Tag> tag) public void UpdateNewTags(IEnumerable<Tag> tag)
{ {
} }
public void UpdateNewTags(Tag tag) public void UpdateNewTags(Tag tag)
{ {
} }
@ -580,7 +585,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
public void RemoveTags(IEnumerable<Tag> tag) public void RemoveTags(IEnumerable<Tag> tag)
{ {
} }
public void RemoveTags(Tag tag) public void RemoveTags(Tag tag)
{ {
} }
@ -589,7 +594,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
{ {
return AsyncEnumerable.Empty<Tag>(); return AsyncEnumerable.Empty<Tag>();
} }
public void MarkAsNew(Guid subject, FileEntry<string> fileEntry) public void MarkAsNew(Guid subject, FileEntry<string> fileEntry)
{ {
} }
@ -597,24 +602,24 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
public async IAsyncEnumerable<Tag> GetNewTagsAsync(Guid subject, Folder<string> parentFolder, bool deepSearch) public async IAsyncEnumerable<Tag> GetNewTagsAsync(Guid subject, Folder<string> parentFolder, bool deepSearch)
{ {
var folderId = DaoSelector.ConvertId(parentFolder.Id); var folderId = DaoSelector.ConvertId(parentFolder.Id);
var entryIDs = await FilesDbContext.ThirdpartyIdMapping var entryIDs = await FilesDbContext.ThirdpartyIdMapping
.AsQueryable() .AsQueryable()
.Where(r => r.Id.StartsWith(parentFolder.Id)) .Where(r => r.Id.StartsWith(parentFolder.Id))
.Select(r => r.HashId) .Select(r => r.HashId)
.ToListAsync() .ToListAsync()
.ConfigureAwait(false); .ConfigureAwait(false);
if (!entryIDs.Any()) if (!entryIDs.Any())
{ {
yield break; yield break;
} }
var q = from r in FilesDbContext.Tag var q = from r in FilesDbContext.Tag
from l in FilesDbContext.TagLink.AsQueryable().Where(a => a.TenantId == r.TenantId && a.TagId == r.Id).DefaultIfEmpty() from l in FilesDbContext.TagLink.AsQueryable().Where(a => a.TenantId == r.TenantId && a.TagId == r.Id).DefaultIfEmpty()
where r.TenantId == TenantID && l.TenantId == TenantID && r.Type == TagType.New && entryIDs.Contains(l.EntryId) where r.TenantId == TenantID && l.TenantId == TenantID && r.Type == TagType.New && entryIDs.Contains(l.EntryId)
select new { tag = r, tagLink = l }; select new { tag = r, tagLink = l };
if (subject != Guid.Empty) if (subject != Guid.Empty)
{ {
q = q.Where(r => r.tag.Owner == subject); q = q.Where(r => r.tag.Owner == subject);
@ -623,7 +628,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
var qList = q var qList = q
.Distinct() .Distinct()
.AsAsyncEnumerable(); .AsAsyncEnumerable();
var tags = qList var tags = qList
.SelectAwait(async r => new Tag .SelectAwait(async r => new Tag
{ {
@ -636,7 +641,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
Id = r.tag.Id Id = r.tag.Id
}); });
if (deepSearch) if (deepSearch)
{ {
await foreach (var e in tags.ConfigureAwait(false)) await foreach (var e in tags.ConfigureAwait(false))
@ -646,7 +651,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
yield break; yield break;
} }
var folderFileIds = new[] { parentFolder.Id } var folderFileIds = new[] { parentFolder.Id }
.Concat(await GetChildrenAsync(folderId).ConfigureAwait(false)); .Concat(await GetChildrenAsync(folderId).ConfigureAwait(false));
@ -659,7 +664,7 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
protected abstract Task<IEnumerable<string>> GetChildrenAsync(string folderId); protected abstract Task<IEnumerable<string>> GetChildrenAsync(string folderId);
#endregion #endregion
public void Dispose() public void Dispose()
{ {
if (ProviderInfo != null) if (ProviderInfo != null)
@ -669,12 +674,12 @@ internal abstract class ThirdPartyProviderDao<T> : ThirdPartyProviderDao, IDispo
} }
} }
} }
internal class ErrorEntry internal class ErrorEntry
{ {
public string Error { get; set; } public string Error { get; set; }
public string ErrorId { get; set; } public string ErrorId { get; set; }
public ErrorEntry(string error, string errorId) public ErrorEntry(string error, string errorId)
{ {
Error = error; Error = error;
@ -687,16 +692,16 @@ public class TagLink
public int TenantId { get; set; } public int TenantId { get; set; }
public int Id { get; set; } public int Id { get; set; }
} }
public class TagLinkComparer : IEqualityComparer<TagLink> public class TagLinkComparer : IEqualityComparer<TagLink>
{ {
public bool Equals([AllowNull] TagLink x, [AllowNull] TagLink y) public bool Equals([AllowNull] TagLink x, [AllowNull] TagLink y)
{ {
return x.Id == y.Id && x.TenantId == y.TenantId; return x.Id == y.Id && x.TenantId == y.TenantId;
} }
public int GetHashCode([DisallowNull] TagLink obj) public int GetHashCode([DisallowNull] TagLink obj)
{ {
return obj.Id.GetHashCode() + obj.TenantId.GetHashCode(); return obj.Id.GetHashCode() + obj.TenantId.GetHashCode();
} }
} }

View File

@ -192,6 +192,7 @@ internal abstract class OneDriveDaoBase : ThirdPartyProviderDao<OneDriveProvider
file.ModifiedOn = onedriveFile.LastModifiedDateTime.HasValue ? _tenantUtil.DateTimeFromUtc(onedriveFile.LastModifiedDateTime.Value.DateTime) : default; file.ModifiedOn = onedriveFile.LastModifiedDateTime.HasValue ? _tenantUtil.DateTimeFromUtc(onedriveFile.LastModifiedDateTime.Value.DateTime) : default;
file.NativeAccessor = onedriveFile; file.NativeAccessor = onedriveFile;
file.Title = MakeItemTitle(onedriveFile); file.Title = MakeItemTitle(onedriveFile);
file.ThumbnailStatus = Thumbnail.Created;
return file; return file;
} }

View File

@ -561,6 +561,12 @@ internal class OneDriveFileDao : OneDriveDaoBase, IFileDao<string>
return false; return false;
} }
public override Task<Stream> GetThumbnailAsync(string fileId, int width, int height)
{
var oneDriveId = MakeOneDriveId(_oneDriveDaoSelector.ConvertId(fileId));
return ProviderInfo.GetThumbnailAsync(oneDriveId, width, height);
}
#region chunking #region chunking
private File<string> RestoreIds(File<string> file) private File<string> RestoreIds(File<string> file)

View File

@ -123,6 +123,12 @@ internal class OneDriveProviderInfo : IProviderInfo
{ {
return _oneDriveProviderInfoHelper.CacheResetAsync(ID, onedriveId); return _oneDriveProviderInfoHelper.CacheResetAsync(ID, onedriveId);
} }
internal async Task<Stream> GetThumbnailAsync(string onedriveId, int width, int height)
{
var storage = await StorageAsync;
return await _oneDriveProviderInfoHelper.GetThumbnailAsync(storage, onedriveId, width, height);
}
} }
[Scope(Additional = typeof(OneDriveProviderInfoExtention))] [Scope(Additional = typeof(OneDriveProviderInfoExtention))]
@ -284,6 +290,11 @@ public class OneDriveProviderInfoHelper
_cacheItem.Remove("onedrive-" + i.Key); _cacheItem.Remove("onedrive-" + i.Key);
} }
} }
internal async Task<Stream> GetThumbnailAsync(OneDriveStorage storage, string onedriveId, int width, int height)
{
return await storage.GetThumbnailAsync(onedriveId, width, height).ConfigureAwait(false);
}
} }
public static class OneDriveProviderInfoExtention public static class OneDriveProviderInfoExtention
{ {

View File

@ -354,6 +354,30 @@ internal class OneDriveStorage
var httpClient = _clientFactory.CreateClient(); var httpClient = _clientFactory.CreateClient();
using var response = await httpClient.SendAsync(request); using var response = await httpClient.SendAsync(request);
} }
public async Task<Stream> GetThumbnailAsync(string fileId, int width, int height)
{
var thumbnails = await OnedriveClient.Drive.Items[fileId].Thumbnails.Request().GetAsync();
if (thumbnails.Count > 0)
{
var url = thumbnails[0].Medium.Url;
url = url.Substring(0, url.IndexOf("?width"));
url = url + $"?width={width}&height={height}&cropmode=none";
var request = new HttpRequestMessage
{
RequestUri = new Uri(url),
Method = HttpMethod.Get
};
var httpClient = _clientFactory.CreateClient();
using var response = await httpClient.SendAsync(request);
var bytes = await response.Content.ReadAsByteArrayAsync();
return new MemoryStream(bytes);
}
else
{
return null;
}
}
} }

View File

@ -464,7 +464,7 @@ internal class ProviderFileDao : ProviderDaoBase, IFileDao<string>
var selector = GetSelector(file.Id); var selector = GetSelector(file.Id);
var fileDao = selector.GetFileDao(file.Id); var fileDao = selector.GetFileDao(file.Id);
return fileDao.UseTrashForRemove(file); return fileDao.UseTrashForRemove(file);
} }
#region chunking #region chunking
@ -520,5 +520,19 @@ internal class ProviderFileDao : ProviderDaoBase, IFileDao<string>
return file; return file;
} }
public override Task<Stream> GetThumbnailAsync(string fileId, int width, int height)
{
var selector = GetSelector(fileId);
var fileDao = selector.GetFileDao(fileId);
return fileDao.GetThumbnailAsync(fileId, width, height);
}
public override Task<Stream> GetThumbnailAsync(File<string> file, int width, int height)
{
var fileDao = GetFileDao(file);
return fileDao.GetThumbnailAsync(file, width, height);
}
#endregion #endregion
} }

View File

@ -65,7 +65,7 @@ public class ThumbnailSettings
private string _formats; private string _formats;
public string Formats public string Formats
{ {
get => _formats ?? ".pptx|.pptm|.ppt|.ppsx|.ppsm|.pps|.potx|.potm|.pot|.odp|.fodp|.otp|.gslides|.xlsx|.xlsm|.xls|.xltx|.xltm|.xlt|.ods|.fods|.ots|.gsheet|.csv|.docx|.docxf|.oform|.docm|.doc|.dotx|.dotm|.dot|.odt|.fodt|.ott|.gdoc|.txt|.rtf|.mht|.html|.htm|.fb2|.epub|.pdf|.djvu|.xps|.oxps|.bmp|.jpeg|.jpg|.png|.gif|.tiff|.tif|.ico"; get => _formats ?? ".pptx|.pptm|.ppt|.ppsx|.ppsm|.pps|.potx|.potm|.pot|.odp|.fodp|.otp|.gslides|.xlsx|.xlsm|.xls|.xltx|.xltm|.xlt|.ods|.fods|.ots|.gsheet|.csv|.docx|.docxf|.oform|.docm|.doc|.dotx|.dotm|.dot|.odt|.fodt|.ott|.gdoc|.txt|.rtf|.mht|.html|.htm|.fb2|.epub|.pdf|.djvu|.xps|.oxps";
set => _formats = value; set => _formats = value;
} }

View File

@ -27,18 +27,18 @@
using JWT.Algorithms; using JWT.Algorithms;
using JWT.Builder; using JWT.Builder;
using JsonException = System.Text.Json.JsonException; using JsonException = System.Text.Json.JsonException;
using MimeMapping = ASC.Common.Web.MimeMapping; using MimeMapping = ASC.Common.Web.MimeMapping;
namespace ASC.Web.Files; namespace ASC.Web.Files;
public class FileHandler public class FileHandler
{ {
public FileHandler(RequestDelegate next) public FileHandler(RequestDelegate next)
{ {
} }
public async Task Invoke(HttpContext context, FileHandlerService fileHandlerService) public async Task Invoke(HttpContext context, FileHandlerService fileHandlerService)
{ {
await fileHandlerService.Invoke(context).ConfigureAwait(false); await fileHandlerService.Invoke(context).ConfigureAwait(false);
} }
@ -47,7 +47,7 @@ public class FileHandler
[Scope] [Scope]
public class FileHandlerService public class FileHandlerService
{ {
private readonly ThumbnailSettings _thumbnailSettings; private readonly ThumbnailSettings _thumbnailSettings;
public string FileHandlerPath public string FileHandlerPath
{ {
@ -109,10 +109,10 @@ public class FileHandlerService
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
TempStream tempStream, TempStream tempStream,
SocketManager socketManager, SocketManager socketManager,
CompressToArchive compressToArchive, CompressToArchive compressToArchive,
InstanceCrypto instanceCrypto, InstanceCrypto instanceCrypto,
IHttpClientFactory clientFactory, IHttpClientFactory clientFactory,
ThumbnailSettings thumbnailSettings) ThumbnailSettings thumbnailSettings)
{ {
_filesLinkUtility = filesLinkUtility; _filesLinkUtility = filesLinkUtility;
_tenantExtra = tenantExtra; _tenantExtra = tenantExtra;
@ -142,7 +142,7 @@ public class FileHandlerService
_userManager = userManager; _userManager = userManager;
_logger = logger; _logger = logger;
_clientFactory = clientFactory; _clientFactory = clientFactory;
this._thumbnailSettings = thumbnailSettings; this._thumbnailSettings = thumbnailSettings;
} }
public Task Invoke(HttpContext context) public Task Invoke(HttpContext context)
@ -225,7 +225,7 @@ public class FileHandlerService
filename = _instanceCrypto.Decrypt(Uri.UnescapeDataString(filename)); filename = _instanceCrypto.Decrypt(Uri.UnescapeDataString(filename));
} }
var path = string.Format(@"{0}\{1}", _securityContext.CurrentAccount.ID, filename); var path = string.Format(@"{0}\{1}", _securityContext.CurrentAccount.ID, filename);
var store = _globalStore.GetStore(); var store = _globalStore.GetStore();
if (!await store.IsFileAsync(FileConstant.StorageDomainTmp, path)) if (!await store.IsFileAsync(FileConstant.StorageDomainTmp, path))
@ -336,7 +336,7 @@ public class FileHandlerService
if (!await fileDao.IsExistOnStorageAsync(file)) if (!await fileDao.IsExistOnStorageAsync(file))
{ {
_logger.ErrorDownloadFile2(file.Id.ToString()); _logger.ErrorDownloadFile2(file.Id.ToString());
context.Response.StatusCode = (int)HttpStatusCode.NotFound; context.Response.StatusCode = (int)HttpStatusCode.NotFound;
return; return;
@ -412,7 +412,7 @@ public class FileHandlerService
{ {
fileStream = await fileDao.GetFileStreamAsync(file); fileStream = await fileDao.GetFileStreamAsync(file);
_logger.InformationConvertingToMp4(file.Title, file.Id.ToString()); _logger.InformationConvertingToMp4(file.Title, file.Id.ToString());
var stream = await _fFmpegService.Convert(fileStream, ext); var stream = await _fFmpegService.Convert(fileStream, ext);
await store.SaveAsync(string.Empty, mp4Path, stream, mp4Name); await store.SaveAsync(string.Empty, mp4Path, stream, mp4Name);
} }
@ -1009,34 +1009,34 @@ public class FileHandlerService
} }
else else
{ {
await ThumbnailFile(context, q.FirstOrDefault() ?? ""); await ThumbnailFileFromThirdparty(context, q.FirstOrDefault() ?? "");
} }
} }
private async Task ThumbnailFile<T>(HttpContext context, T id) private async Task ThumbnailFile(HttpContext context, int id)
{ {
try try
{ {
var defaultSize = _thumbnailSettings.Sizes.FirstOrDefault(); var defaultSize = _thumbnailSettings.Sizes.FirstOrDefault();
if (defaultSize == null) if (defaultSize == null)
{ {
context.Response.StatusCode = (int)HttpStatusCode.NotFound; context.Response.StatusCode = (int)HttpStatusCode.NotFound;
return; return;
} }
var width = defaultSize.Width; var width = defaultSize.Width;
var height = defaultSize.Height; var height = defaultSize.Height;
var size = context.Request.Query["size"].ToString() ?? ""; var size = context.Request.Query["size"].ToString() ?? "";
var sizes = size.Split('x'); var sizes = size.Split('x');
if (sizes.Length == 2) if (sizes.Length == 2)
{ {
_ = int.TryParse(sizes[0], out width); _ = int.TryParse(sizes[0], out width);
_ = int.TryParse(sizes[1], out height); _ = int.TryParse(sizes[1], out height);
} }
var fileDao = _daoFactory.GetFileDao<T>(); var fileDao = _daoFactory.GetFileDao<int>();
var file = int.TryParse(context.Request.Query[FilesLinkUtility.Version], out var version) && version > 0 var file = int.TryParse(context.Request.Query[FilesLinkUtility.Version], out var version) && version > 0
? await fileDao.GetFileAsync(id, version) ? await fileDao.GetFileAsync(id, version)
: await fileDao.GetFileAsync(id); : await fileDao.GetFileAsync(id);
@ -1069,7 +1069,7 @@ public class FileHandlerService
context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue("." + _global.ThumbnailExtension)); context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue("." + _global.ThumbnailExtension));
context.Response.ContentType = MimeMapping.GetMimeMapping("." + _global.ThumbnailExtension); context.Response.ContentType = MimeMapping.GetMimeMapping("." + _global.ThumbnailExtension);
using (var stream = await fileDao.GetThumbnailAsync(file, width, height)) using (var stream = await fileDao.GetThumbnailAsync(file, width, height))
{ {
context.Response.Headers.Add("Content-Length", stream.Length.ToString(CultureInfo.InvariantCulture)); context.Response.Headers.Add("Content-Length", stream.Length.ToString(CultureInfo.InvariantCulture));
await stream.CopyToAsync(context.Response.Body); await stream.CopyToAsync(context.Response.Body);
@ -1101,9 +1101,68 @@ public class FileHandlerService
} }
} }
private async Task ThumbnailFileFromThirdparty(HttpContext context, string id)
{
try
{
var defaultSize = _thumbnailSettings.Sizes.FirstOrDefault();
if (defaultSize == null)
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
var width = defaultSize.Width;
var height = defaultSize.Height;
var size = context.Request.Query["size"].ToString() ?? "";
var sizes = size.Split('x');
if (sizes.Length == 2)
{
_ = int.TryParse(sizes[0], out width);
_ = int.TryParse(sizes[1], out height);
}
context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue("." + _global.ThumbnailExtension));
context.Response.ContentType = MimeMapping.GetMimeMapping("." + _global.ThumbnailExtension);
var fileDao = _daoFactory.GetFileDao<string>();
using (var stream = await fileDao.GetThumbnailAsync(id, width, height))
{
await stream.CopyToAsync(context.Response.Body);
}
}
catch (FileNotFoundException ex)
{
_logger.ErrorForUrl(context.Request.Url(), ex);
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
await context.Response.WriteAsync(ex.Message);
return;
}
catch (Exception ex)
{
_logger.ErrorForUrl(context.Request.Url(), ex);
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
await context.Response.WriteAsync(ex.Message);
return;
}
try
{
await context.Response.Body.FlushAsync();
await context.Response.CompleteAsync();
}
catch (HttpException he)
{
_logger.ErrorThumbnail(he);
}
}
private static string GetEtag<T>(File<T> file) private static string GetEtag<T>(File<T> file)
{ {
return file.Id + ":" + file.Version + ":" + file.Title.GetHashCode() + ":" + file.ContentLength; return file.Id + ":" + file.Version + ":" + file.Title.GetHashCode() + ":" + file.ContentLength;
} }
private Task CreateFile(HttpContext context) private Task CreateFile(HttpContext context)
@ -1194,33 +1253,33 @@ public class FileHandlerService
context.Response.Redirect( context.Response.Redirect(
(context.Request.Query["openfolder"].FirstOrDefault() ?? "").Equals("true") (context.Request.Query["openfolder"].FirstOrDefault() ?? "").Equals("true")
? await _pathProvider.GetFolderUrlByIdAsync(file.ParentId) ? await _pathProvider.GetFolderUrlByIdAsync(file.ParentId)
: (_filesLinkUtility.GetFileWebEditorUrl(file.Id) + "#message/" + HttpUtility.UrlEncode(string.Format(FilesCommonResource.MessageFileCreated, folder.Title)))); : (_filesLinkUtility.GetFileWebEditorUrl(file.Id) + "#message/" + HttpUtility.UrlEncode(string.Format(FilesCommonResource.MessageFileCreated, folder.Title))));
} }
private async Task InternalWriteError(HttpContext context, Exception ex, bool responseMessage) private async Task InternalWriteError(HttpContext context, Exception ex, bool responseMessage)
{ {
_logger.ErrorFileHandler(ex); _logger.ErrorFileHandler(ex);
if (responseMessage) if (responseMessage)
{ {
await context.Response.WriteAsync("error: " + ex.Message); await context.Response.WriteAsync("error: " + ex.Message);
return; return;
} }
context.Response.Redirect(PathProvider.StartURL + "#error/" + HttpUtility.UrlEncode(ex.Message), true); context.Response.Redirect(PathProvider.StartURL + "#error/" + HttpUtility.UrlEncode(ex.Message), true);
return; return;
} }
private Task InternalWriteOk<T>(HttpContext context, Folder<T> folder, File<T> file) private Task InternalWriteOk<T>(HttpContext context, Folder<T> folder, File<T> file)
{ {
var message = string.Format(FilesCommonResource.MessageFileCreated, folder.Title); var message = string.Format(FilesCommonResource.MessageFileCreated, folder.Title);
if (_fileUtility.CanWebRestrictedEditing(file.Title)) if (_fileUtility.CanWebRestrictedEditing(file.Title))
{ {
message = string.Format(FilesCommonResource.MessageFileCreatedForm, folder.Title); message = string.Format(FilesCommonResource.MessageFileCreatedForm, folder.Title);
} }
return context.Response.WriteAsync("ok: " + message); return context.Response.WriteAsync("ok: " + message);
} }
private async Task<File<T>> CreateFileFromTemplateAsync<T>(Folder<T> folder, string fileTitle, string docType) private async Task<File<T>> CreateFileFromTemplateAsync<T>(Folder<T> folder, string fileTitle, string docType)
{ {
@ -1260,7 +1319,7 @@ public class FileHandlerService
var file = _serviceProvider.GetService<File<T>>(); var file = _serviceProvider.GetService<File<T>>();
file.Title = fileTitle; file.Title = fileTitle;
file.ParentId = folder.Id; file.ParentId = folder.Id;
file.Comment = FilesCommonResource.CommentCreate; file.Comment = FilesCommonResource.CommentCreate;
var fileDao = _daoFactory.GetFileDao<T>(); var fileDao = _daoFactory.GetFileDao<T>();
@ -1278,7 +1337,7 @@ public class FileHandlerService
var file = _serviceProvider.GetService<File<T>>(); var file = _serviceProvider.GetService<File<T>>();
file.Title = fileTitle; file.Title = fileTitle;
file.ParentId = folder.Id; file.ParentId = folder.Id;
file.Comment = FilesCommonResource.CommentCreate; file.Comment = FilesCommonResource.CommentCreate;
var request = new HttpRequestMessage var request = new HttpRequestMessage
@ -1351,7 +1410,7 @@ public class FileHandlerService
return; return;
} }
urlRedirect = _filesLinkUtility.GetFileWebPreviewUrl(_fileUtility, file.Title, file.Id); urlRedirect = _filesLinkUtility.GetFileWebPreviewUrl(_fileUtility, file.Title, file.Id);
} }
if (string.IsNullOrEmpty(urlRedirect)) if (string.IsNullOrEmpty(urlRedirect))
@ -1515,4 +1574,4 @@ public static class FileHandlerExtensions
{ {
return builder.UseMiddleware<FileHandler>(); return builder.UseMiddleware<FileHandler>();
} }
} }

View File

@ -30,6 +30,9 @@ internal static partial class FFmpegServiceLogger
[LoggerMessage(Level = LogLevel.Error, Message = "FFmpeg/avconv was not found in PATH or 'files.ffmpeg' setting")] [LoggerMessage(Level = LogLevel.Error, Message = "FFmpeg/avconv was not found in PATH or 'files.ffmpeg' setting")]
public static partial void ErrorFFmpeg(this ILogger<FFmpegService> logger); public static partial void ErrorFFmpeg(this ILogger<FFmpegService> logger);
[LoggerMessage(Level = LogLevel.Information, Message = "FFmpeg found in {path}")] [LoggerMessage(Level = LogLevel.Error, Message = "File {file} not found")]
public static partial void ErrorFileNotFound(this ILogger<FFmpegService> logger, string file);
[LoggerMessage(Level = LogLevel.Information, Message = "FFmpeg found in {path}")]
public static partial void InformationFFmpegFoundIn(this ILogger<FFmpegService> logger, string path); public static partial void InformationFFmpegFoundIn(this ILogger<FFmpegService> logger, string path);
} }

View File

@ -44,11 +44,25 @@ public class FFmpegService
} }
} }
private readonly List<string> _convertableMedia;
private readonly List<string> _fFmpegExecutables = new List<string>() { "ffmpeg", "avconv" };
private readonly string _fFmpegPath;
private readonly string _fFmpegArgs;
private readonly string _fFmpegThumbnailsArgs;
private readonly List<string> _fFmpegFormats;
private readonly ILogger<FFmpegService> _logger;
public bool IsConvertable(string extension) public bool IsConvertable(string extension)
{ {
return MustConvertable.Contains(extension.TrimStart('.')); return MustConvertable.Contains(extension.TrimStart('.'));
} }
public bool ExistFormat(string extension)
{
return _fFmpegFormats.Contains(extension);
}
public Task<Stream> Convert(Stream inputStream, string inputFormat) public Task<Stream> Convert(Stream inputStream, string inputFormat)
{ {
if (inputStream == null) if (inputStream == null)
@ -68,17 +82,13 @@ public class FFmpegService
{ {
var startInfo = PrepareFFmpeg(inputFormat); var startInfo = PrepareFFmpeg(inputFormat);
Process process; using var process = Process.Start(startInfo);
using (process = new Process { StartInfo = startInfo })
{
process.Start();
await StreamCopyToAsync(inputStream, process.StandardInput.BaseStream, closeDst: true); await inputStream.CopyToAsync(process.StandardInput.BaseStream);
await ProcessLog(process.StandardError.BaseStream); await ProcessLog(process.StandardError.BaseStream);
return process.StandardOutput.BaseStream; return process.StandardOutput.BaseStream;
}
} }
public FFmpegService(ILogger<FFmpegService> logger, IConfiguration configuration) public FFmpegService(ILogger<FFmpegService> logger, IConfiguration configuration)
@ -86,6 +96,8 @@ public class FFmpegService
_logger = logger; _logger = logger;
_fFmpegPath = configuration["files:ffmpeg:value"]; _fFmpegPath = configuration["files:ffmpeg:value"];
_fFmpegArgs = configuration["files:ffmpeg:args"] ?? "-i - -preset ultrafast -movflags frag_keyframe+empty_moov -f {0} -"; _fFmpegArgs = configuration["files:ffmpeg:args"] ?? "-i - -preset ultrafast -movflags frag_keyframe+empty_moov -f {0} -";
_fFmpegThumbnailsArgs = configuration["files:ffmpeg:thumbnails:args"] ?? "-ss 3 -i \"{0}\" -vf \"thumbnail\" -frames:v 1 -vsync vfr \"{1}\" -y";
_fFmpegFormats = configuration.GetSection("files:ffmpeg:thumbnails:formats").Get<List<string>>() ?? FileUtility.ExtsVideo;
_convertableMedia = (configuration.GetSection("files:ffmpeg:exts").Get<string[]>() ?? Array.Empty<string>()).ToList(); _convertableMedia = (configuration.GetSection("files:ffmpeg:exts").Get<string[]>() ?? Array.Empty<string>()).ToList();
@ -120,13 +132,6 @@ public class FFmpegService
} }
} }
private readonly List<string> _convertableMedia;
private readonly List<string> _fFmpegExecutables = new List<string>() { "ffmpeg", "avconv" };
private readonly string _fFmpegPath;
private readonly string _fFmpegArgs;
private readonly ILogger<FFmpegService> _logger;
private ProcessStartInfo PrepareFFmpeg(string inputFormat) private ProcessStartInfo PrepareFFmpeg(string inputFormat)
{ {
if (!_convertableMedia.Contains(inputFormat.TrimStart('.'))) if (!_convertableMedia.Contains(inputFormat.TrimStart('.')))
@ -134,6 +139,15 @@ public class FFmpegService
throw new ArgumentException("input format"); throw new ArgumentException("input format");
} }
var startInfo = PrepareCommonFFmpeg();
startInfo.Arguments = string.Format(_fFmpegArgs, "mp4");
return startInfo;
}
private ProcessStartInfo PrepareCommonFFmpeg()
{
var startInfo = new ProcessStartInfo(); var startInfo = new ProcessStartInfo();
if (string.IsNullOrEmpty(_fFmpegPath)) if (string.IsNullOrEmpty(_fFmpegPath))
@ -143,56 +157,15 @@ public class FFmpegService
} }
startInfo.FileName = _fFmpegPath; startInfo.FileName = _fFmpegPath;
startInfo.WorkingDirectory = Path.GetDirectoryName(_fFmpegPath);
startInfo.Arguments = string.Format(_fFmpegArgs, "mp4");
startInfo.UseShellExecute = false; startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true; startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true; startInfo.RedirectStandardInput = true;
startInfo.RedirectStandardError = true; startInfo.RedirectStandardError = true;
startInfo.CreateNoWindow = true; startInfo.CreateNoWindow = true;
startInfo.WindowStyle = ProcessWindowStyle.Normal; startInfo.WindowStyle = ProcessWindowStyle.Normal;
return startInfo; return startInfo;
} }
private static Task<int> StreamCopyToAsync(Stream srcStream, Stream dstStream, bool closeSrc = false, bool closeDst = false)
{
ArgumentNullException.ThrowIfNull(srcStream);
ArgumentNullException.ThrowIfNull(dstStream);
return StreamCopyToAsyncInternal(srcStream, dstStream, closeSrc, closeDst);
}
private static async Task<int> StreamCopyToAsyncInternal(Stream srcStream, Stream dstStream, bool closeSrc, bool closeDst)
{
const int bufs = 2048 * 4;
var buffer = new byte[bufs];
int readed;
var total = 0;
while ((readed = await srcStream.ReadAsync(buffer, 0, bufs)) > 0)
{
await dstStream.WriteAsync(buffer, 0, readed);
await dstStream.FlushAsync();
total += readed;
}
if (closeSrc)
{
srcStream.Dispose();
srcStream.Close();
}
if (closeDst)
{
await dstStream.FlushAsync();
dstStream.Dispose();
dstStream.Close();
}
return total;
}
private async Task ProcessLog(Stream stream) private async Task ProcessLog(Stream stream)
{ {
using var reader = new StreamReader(stream, Encoding.UTF8); using var reader = new StreamReader(stream, Encoding.UTF8);
@ -202,4 +175,15 @@ public class FFmpegService
_logger.Information(line); _logger.Information(line);
} }
} }
public async Task CreateThumbnail(string sourcePath, string destPath)
{
var startInfo = PrepareCommonFFmpeg();
startInfo.Arguments = string.Format(_fFmpegThumbnailsArgs, sourcePath, destPath);
using var process = Process.Start(startInfo);
await ProcessLog(process.StandardError.BaseStream);
}
} }

View File

@ -61,7 +61,7 @@ public class FilesControllerThirdparty : FilesController<string>
_documentServiceHelper = documentServiceHelper; _documentServiceHelper = documentServiceHelper;
} }
[HttpGet("file/app-{fileId}", Order = int.MaxValue)] [HttpGet("file/app-{fileId}", Order = 1)]
public async Task<FileEntryDto> GetFileInfoThirdPartyAsync(string fileId) public async Task<FileEntryDto> GetFileInfoThirdPartyAsync(string fileId)
{ {
fileId = "app-" + fileId; fileId = "app-" + fileId;

View File

@ -104,7 +104,7 @@ public abstract class UploadController<T> : ApiControllerBase
/// <param name="keepConvertStatus" visible="false">Keep status conversation after finishing</param> /// <param name="keepConvertStatus" visible="false">Keep status conversation after finishing</param>
/// <category>Uploads</category> /// <category>Uploads</category>
/// <returns></returns> /// <returns></returns>
[HttpPost("{folderId}/insert", Order = int.MaxValue)] [HttpPost("{folderId}/insert", Order = 1)]
public Task<FileDto<T>> InsertFileAsync(T folderId, [FromForm][ModelBinder(BinderType = typeof(InsertFileModelBinder))] InsertFileRequestDto inDto) public Task<FileDto<T>> InsertFileAsync(T folderId, [FromForm][ModelBinder(BinderType = typeof(InsertFileModelBinder))] InsertFileRequestDto inDto)
{ {
return _filesControllerHelper.InsertFileAsync(folderId, inDto.Stream, inDto.Title, inDto.CreateNewIfExist, inDto.KeepConvertStatus); return _filesControllerHelper.InsertFileAsync(folderId, inDto.Stream, inDto.Title, inDto.CreateNewIfExist, inDto.KeepConvertStatus);
@ -133,7 +133,7 @@ public abstract class UploadController<T> : ApiControllerBase
/// <param name="storeOriginalFileFlag" visible="false">If True, upload documents in original formats as well</param> /// <param name="storeOriginalFileFlag" visible="false">If True, upload documents in original formats as well</param>
/// <param name="keepConvertStatus" visible="false">Keep status conversation after finishing</param> /// <param name="keepConvertStatus" visible="false">Keep status conversation after finishing</param>
/// <returns>Uploaded file</returns> /// <returns>Uploaded file</returns>
[HttpPost("{folderId}/upload", Order = int.MaxValue)] [HttpPost("{folderId}/upload", Order = 1)]
public Task<object> UploadFileAsync(T folderId, [ModelBinder(BinderType = typeof(UploadModelBinder))] UploadRequestDto inDto) public Task<object> UploadFileAsync(T folderId, [ModelBinder(BinderType = typeof(UploadModelBinder))] UploadRequestDto inDto)
{ {
return _filesControllerHelper.UploadFileAsync(folderId, inDto); return _filesControllerHelper.UploadFileAsync(folderId, inDto);

View File

@ -0,0 +1,54 @@
// (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.Files.Expired;
[Singletone]
public class DeleteExpiredService : BackgroundService
{
private readonly CommonChunkedUploadSessionHolder _commonChunkedUploadSessionHolder;
private readonly TimeSpan _launchFrequency;
public DeleteExpiredService(
ILogger<DeleteExpiredService> log,
SetupInfo setupInfo,
TempPath tempPath,
GlobalStore globalStore,
IConfiguration configuration)
{
_launchFrequency = TimeSpan.Parse(configuration["files:deleteExpired"] ?? "1", CultureInfo.InvariantCulture);
_commonChunkedUploadSessionHolder = new CommonChunkedUploadSessionHolder(tempPath, log, globalStore.GetStore(false), FileConstant.StorageDomainTmp, setupInfo.ChunkUploadSize);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _commonChunkedUploadSessionHolder.DeleteExpiredAsync();
await Task.Delay(_launchFrequency, stoppingToken);
}
}
}

View File

@ -24,62 +24,61 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // 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 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
global using System.Collections.Concurrent; global using System.Collections.Concurrent;
global using System.Globalization; global using System.Globalization;
global using System.Linq.Expressions; global using System.Linq.Expressions;
global using System.Reflection; global using System.Reflection;
global using System.Text.Json; global using System.Text.Json;
global using ASC.Api.Core; global using ASC.Api.Core;
global using ASC.Api.Core.Extensions; global using ASC.Api.Core.Extensions;
global using ASC.Common; global using ASC.Common;
global using ASC.Common.Caching; global using ASC.Common.Caching;
global using ASC.Common.DependencyInjection; global using ASC.Common.DependencyInjection;
global using ASC.Common.Log; global using ASC.Common.Log;
global using ASC.Common.Mapping; global using ASC.Common.Mapping;
global using ASC.Common.Utils; global using ASC.Core;
global using ASC.Core; global using ASC.Core.ChunkedUploader;
global using ASC.Core.Common; global using ASC.Core.Common;
global using ASC.Core.Common.EF; global using ASC.Core.Common.EF;
global using ASC.Core.Common.Hosting; global using ASC.Core.Common.Hosting;
global using ASC.Core.Common.Hosting.Interfaces; global using ASC.Core.Common.Hosting.Interfaces;
global using ASC.Core.Tenants; global using ASC.Core.Tenants;
global using ASC.ElasticSearch; global using ASC.ElasticSearch;
global using ASC.ElasticSearch.Service; global using ASC.ElasticSearch.Service;
global using ASC.EventBus.Abstractions; global using ASC.EventBus.Abstractions;
global using ASC.EventBus.Log; global using ASC.EventBus.Log;
global using ASC.Feed; global using ASC.Feed;
global using ASC.Feed.Aggregator.Service; global using ASC.Feed.Aggregator.Service;
global using ASC.Feed.Core; global using ASC.Feed.Core;
global using ASC.Feed.Data; global using ASC.Feed.Data;
global using ASC.Files.AutoCleanUp; global using ASC.Files.AutoCleanUp;
global using ASC.Files.Core; global using ASC.Files.Core;
global using ASC.Files.Core.Core.Entries; global using ASC.Files.Core.Core.Entries;
global using ASC.Files.Core.EF; global using ASC.Files.Core.EF;
global using ASC.Files.Core.IntegrationEvents.Events; global using ASC.Files.Core.IntegrationEvents.Events;
global using ASC.Files.Core.Log; global using ASC.Files.Core.Log;
global using ASC.Files.Core.Resources; global using ASC.Files.Core.Resources;
global using ASC.Files.Core.Security; global using ASC.Files.Core.Security;
global using ASC.Files.Expired;
global using ASC.Files.Service.Log; global using ASC.Files.Service.Log;
global using ASC.Files.ThumbnailBuilder; global using ASC.Files.ThumbnailBuilder;
global using ASC.Thumbnail.IntegrationEvents.EventHandling; global using ASC.Thumbnail.IntegrationEvents.EventHandling;
global using ASC.Web.Core; global using ASC.Web.Core;
global using ASC.Web.Core.Files; global using ASC.Web.Core.Files;
global using ASC.Web.Core.Users; global using ASC.Web.Files.Classes;
global using ASC.Web.Files.Classes; global using ASC.Web.Files.Core.Search;
global using ASC.Web.Files.Core; global using ASC.Web.Files.Services.DocumentService;
global using ASC.Web.Files.Core.Search; global using ASC.Web.Files.Services.FFmpegService;
global using ASC.Web.Files.Services.DocumentService; global using ASC.Web.Files.Utils;
global using ASC.Web.Files.Utils; global using ASC.Web.Studio.Core;
global using ASC.Web.Studio.Core;
global using Autofac; global using Autofac;
global using Microsoft.AspNetCore.Builder; global using Microsoft.AspNetCore.Builder;
global using Microsoft.Extensions.Hosting.WindowsServices; global using Microsoft.Extensions.Hosting.WindowsServices;
global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Logging;
global using SixLabors.ImageSharp; global using SixLabors.ImageSharp;
global using SixLabors.ImageSharp.Formats.Png;
global using static ASC.Web.Core.Files.DocumentService; global using static ASC.Web.Core.Files.DocumentService;

View File

@ -82,6 +82,9 @@ builder.Host.ConfigureDefault(args, (hostContext, config, env, path) =>
services.AddHostedService<Launcher>(); services.AddHostedService<Launcher>();
diHelper.TryAdd<Launcher>(); diHelper.TryAdd<Launcher>();
services.AddHostedService<DeleteExpiredService>();
diHelper.TryAdd<DeleteExpiredService>();
diHelper.TryAdd<AuthManager>(); diHelper.TryAdd<AuthManager>();
diHelper.TryAdd<BaseCommonLinkUtility>(); diHelper.TryAdd<BaseCommonLinkUtility>();
diHelper.TryAdd<FeedAggregateDataProvider>(); diHelper.TryAdd<FeedAggregateDataProvider>();

View File

@ -42,7 +42,7 @@ public class BuilderQueue<T>
_config = settings; _config = settings;
} }
public async Task BuildThumbnails(IEnumerable<FileData<T>> filesWithoutThumbnails) public async Task BuildThumbnails(IEnumerable<FileData<T>> filesWithoutThumbnails)
{ {
try try
{ {
@ -70,18 +70,25 @@ public class BuilderQueue<T>
[Scope] [Scope]
public class Builder<T> public class Builder<T>
{ {
private readonly ThumbnailSettings _config; private readonly ThumbnailSettings _config;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly TenantManager _tenantManager; private readonly TenantManager _tenantManager;
private readonly IDaoFactory _daoFactory; private readonly IDaoFactory _daoFactory;
private readonly DocumentServiceConnector _documentServiceConnector; private readonly DocumentServiceConnector _documentServiceConnector;
private readonly DocumentServiceHelper _documentServiceHelper; private readonly DocumentServiceHelper _documentServiceHelper;
private readonly Global _global; private readonly Global _global;
private readonly PathProvider _pathProvider; private readonly PathProvider _pathProvider;
private readonly IHttpClientFactory _clientFactory; private readonly IHttpClientFactory _clientFactory;
private readonly ThumbnailSettings _thumbnailSettings; private readonly ThumbnailSettings _thumbnailSettings;
private readonly SocketManager _socketManager; private readonly SocketManager _socketManager;
private readonly FFmpegService _fFmpegService;
private readonly TempPath _tempPath;
private readonly List<string> _imageFormatsCanBeCrop = new List<string>
{
".bmp", ".gif", ".jpeg", ".jpg", ".pbm", ".png", ".tiff", ".tga", ".webp",
};
public Builder( public Builder(
ThumbnailSettings settings, ThumbnailSettings settings,
TenantManager tenantManager, TenantManager tenantManager,
@ -92,8 +99,10 @@ public class Builder<T>
PathProvider pathProvider, PathProvider pathProvider,
ILoggerProvider log, ILoggerProvider log,
IHttpClientFactory clientFactory, IHttpClientFactory clientFactory,
FFmpegService fFmpegService,
TempPath tempPath,
SocketManager socketManager, SocketManager socketManager,
ThumbnailSettings thumbnailSettings) ThumbnailSettings thumbnailSettings)
{ {
_config = settings; _config = settings;
_tenantManager = tenantManager; _tenantManager = tenantManager;
@ -104,11 +113,13 @@ public class Builder<T>
_pathProvider = pathProvider; _pathProvider = pathProvider;
_logger = log.CreateLogger("ASC.Files.ThumbnailBuilder"); _logger = log.CreateLogger("ASC.Files.ThumbnailBuilder");
_clientFactory = clientFactory; _clientFactory = clientFactory;
_fFmpegService = fFmpegService;
_tempPath = tempPath;
_socketManager = socketManager; _socketManager = socketManager;
_thumbnailSettings = thumbnailSettings; _thumbnailSettings = thumbnailSettings;
} }
internal async Task BuildThumbnail(FileData<T> fileData) internal async Task BuildThumbnail(FileData<T> fileData)
{ {
try try
{ {
@ -122,7 +133,7 @@ public class Builder<T>
return; return;
} }
await GenerateThumbnail(fileDao, fileData); await GenerateThumbnail(fileDao, fileData);
} }
catch (Exception exception) catch (Exception exception)
{ {
@ -134,13 +145,13 @@ public class Builder<T>
} }
} }
private async Task GenerateThumbnail(IFileDao<T> fileDao, FileData<T> fileData) private async Task GenerateThumbnail(IFileDao<T> fileDao, FileData<T> fileData)
{ {
File<T> file = null; File<T> file = null;
try try
{ {
file = await fileDao.GetFileAsync(fileData.FileId); file = await fileDao.GetFileAsync(fileData.FileId);
if (file == null) if (file == null)
{ {
@ -158,29 +169,37 @@ public class Builder<T>
var ext = FileUtility.GetFileExtension(file.Title); var ext = FileUtility.GetFileExtension(file.Title);
if (!_config.FormatsArray.Contains(ext) || file.Encrypted || file.RootFolderType == FolderType.TRASH || file.ContentLength > _config.AvailableFileSize) if (!CanCreateThumbnail(ext) || file.Encrypted || file.RootFolderType == FolderType.TRASH || file.ContentLength > _config.AvailableFileSize)
{ {
file.ThumbnailStatus = ASC.Files.Core.Thumbnail.NotRequired; file.ThumbnailStatus = ASC.Files.Core.Thumbnail.NotRequired;
foreach (var size in _thumbnailSettings.Sizes) foreach (var size in _thumbnailSettings.Sizes)
{ {
await fileDao.SaveThumbnailAsync(file, null, size.Width, size.Height); await fileDao.SaveThumbnailAsync(file, null, size.Width, size.Height);
} }
return; return;
} }
if (IsImage(file)) if (IsVideo(ext))
{ {
await CropImage(fileDao, file); await MakeThumbnailFromVideo(fileDao, file);
} }
else else
{ {
await MakeThumbnail(fileDao, file);
if (IsImage(ext))
{
await CropImage(fileDao, file);
}
else
{
await MakeThumbnail(fileDao, file);
}
} }
var newFile = await fileDao.GetFileStableAsync(file.Id); var newFile = await fileDao.GetFileStableAsync(file.Id);
await _socketManager.UpdateFileAsync(newFile); await _socketManager.UpdateFileAsync(newFile);
} }
catch (Exception exception) catch (Exception exception)
{ {
@ -194,9 +213,32 @@ public class Builder<T>
} }
} }
} }
} }
private async Task MakeThumbnail(IFileDao<T> fileDao, File<T> file) private async Task MakeThumbnailFromVideo(IFileDao<T> fileDao, File<T> file)
{
var streamFile = await fileDao.GetFileStreamAsync(file);
var thumbPath = _tempPath.GetTempFileName("jpg");
var tempFilePath = _tempPath.GetTempFileName(Path.GetExtension(file.Title));
using (var fileStream = new FileStream(tempFilePath, FileMode.Open, FileAccess.ReadWrite, System.IO.FileShare.Read))
{
await streamFile.CopyToAsync(fileStream);
}
await _fFmpegService.CreateThumbnail(tempFilePath, thumbPath);
using (var streamThumb = new FileStream(thumbPath, FileMode.Open, FileAccess.ReadWrite, System.IO.FileShare.Read))
{
await Crop(fileDao, file, streamThumb);
}
File.Delete(thumbPath);
File.Delete(tempFilePath);
}
private async Task MakeThumbnail(IFileDao<T> fileDao, File<T> file)
{ {
foreach (var w in _config.Sizes) foreach (var w in _config.Sizes)
{ {
@ -254,7 +296,7 @@ public class Builder<T>
} }
} }
private async Task<(bool, string)> GetThumbnailUrl(File<T> file, string toExtension, int width, int height) private async Task<(bool, string)> GetThumbnailUrl(File<T> file, string toExtension, int width, int height)
{ {
var fileUri = _pathProvider.GetFileStreamUrl(file); var fileUri = _pathProvider.GetFileStreamUrl(file);
fileUri = _documentServiceConnector.ReplaceCommunityAdress(fileUri); fileUri = _documentServiceConnector.ReplaceCommunityAdress(fileUri);
@ -273,7 +315,7 @@ public class Builder<T>
IgnorePrintArea = true, IgnorePrintArea = true,
//Orientation = "landscape", // "297mm" x "210mm" //Orientation = "landscape", // "297mm" x "210mm"
FitToHeight = height, FitToHeight = height,
FitToWidth = width, FitToWidth = width,
Headings = false, Headings = false,
GridLines = false, GridLines = false,
Margins = new DocumentService.SpreadsheetLayout.LayoutMargins Margins = new DocumentService.SpreadsheetLayout.LayoutMargins
@ -288,54 +330,62 @@ public class Builder<T>
} }
}; };
var (operationResultProgress, url) = await _documentServiceConnector.GetConvertedUriAsync(fileUri, fileExtension, toExtension, docKey, null, CultureInfo.CurrentCulture.Name, thumbnail, spreadsheetLayout, false); var (operationResultProgress, url) = await _documentServiceConnector.GetConvertedUriAsync(fileUri, fileExtension, toExtension, docKey, null, CultureInfo.CurrentCulture.Name, thumbnail, spreadsheetLayout, false);
operationResultProgress = Math.Min(operationResultProgress, 100); operationResultProgress = Math.Min(operationResultProgress, 100);
return (operationResultProgress == 100, url); return (operationResultProgress == 100, url);
} }
private async Task SaveThumbnail(IFileDao<T> fileDao, File<T> file, string thumbnailUrl, int width, int height) private async Task SaveThumbnail(IFileDao<T> fileDao, File<T> file, string thumbnailUrl, int width, int height)
{ {
_logger.DebugMakeThumbnail3(file.Id.ToString(), thumbnailUrl); _logger.DebugMakeThumbnail3(file.Id.ToString(), thumbnailUrl);
using var request = new HttpRequestMessage(); using var request = new HttpRequestMessage();
request.RequestUri = new Uri(thumbnailUrl); request.RequestUri = new Uri(thumbnailUrl);
var httpClient = _clientFactory.CreateClient(); var httpClient = _clientFactory.CreateClient();
using var response = httpClient.Send(request); using var response = httpClient.Send(request);
using (var stream = await response.Content.ReadAsStreamAsync()) using (var stream = await response.Content.ReadAsStreamAsync())
{ {
using (var sourceImg = await Image.LoadAsync(stream)) using (var sourceImg = await Image.LoadAsync(stream))
{ {
await CropAsync(sourceImg, fileDao, file, width, height); await CropAsync(sourceImg, fileDao, file, width, height);
} }
} }
_logger.DebugMakeThumbnail4(file.Id.ToString()); _logger.DebugMakeThumbnail4(file.Id.ToString());
} }
private bool IsImage(File<T> file) private bool CanCreateThumbnail(string extention)
{ {
var extension = FileUtility.GetFileExtension(file.Title); return _config.FormatsArray.Contains(extention) || IsVideo(extention) || IsImage(extention);
return FileUtility.ExtsImage.Contains(extension);
} }
private async Task CropImage(IFileDao<T> fileDao, File<T> file) private bool IsImage(string extention)
{
return _imageFormatsCanBeCrop.Contains(extention);
}
private bool IsVideo(string extention)
{
return _fFmpegService.ExistFormat(extention);
}
private async Task CropImage(IFileDao<T> fileDao, File<T> file)
{ {
_logger.DebugCropImage(file.Id.ToString()); _logger.DebugCropImage(file.Id.ToString());
using (var stream = await fileDao.GetFileStreamAsync(file)) using (var stream = await fileDao.GetFileStreamAsync(file))
{ {
await Crop(fileDao, file, stream); await Crop(fileDao, file, stream);
} }
_logger.DebugCropImageSuccessfullySaved(file.Id.ToString()); _logger.DebugCropImageSuccessfullySaved(file.Id.ToString());
} }
private async Task Crop(IFileDao<T> fileDao, File<T> file, Stream stream) private async Task Crop(IFileDao<T> fileDao, File<T> file, Stream stream)
{ {
using (var sourceImg = await Image.LoadAsync(stream)) using (var sourceImg = await Image.LoadAsync(stream))
{ {
//var tasks = new List<Task>(); //var tasks = new List<Task>();
@ -357,12 +407,11 @@ public class Builder<T>
GC.Collect(); GC.Collect();
} }
private async ValueTask CropAsync(Image sourceImg, IFileDao<T> fileDao, File<T> file, int width, int height) private async ValueTask CropAsync(Image sourceImg, IFileDao<T> fileDao, File<T> file, int width, int height)
{ {
var targetSize = new Size(Math.Min(sourceImg.Width, width), Math.Min(sourceImg.Height, height)); using var targetImg = GetImageThumbnail(sourceImg, width);
using var targetImg = GetImageThumbnail(sourceImg, targetSize, width, height);
using var targetStream = new MemoryStream(); using var targetStream = new MemoryStream();
switch (_global.ThumbnailExtension) switch (_global.ThumbnailExtension)
{ {
case ThumbnailExtension.bmp: case ThumbnailExtension.bmp:
await targetImg.SaveAsBmpAsync(targetStream); await targetImg.SaveAsBmpAsync(targetStream);
@ -392,8 +441,8 @@ public class Builder<T>
await fileDao.SaveThumbnailAsync(file, targetStream, width, height); await fileDao.SaveThumbnailAsync(file, targetStream, width, height);
} }
private Image GetImageThumbnail(Image sourceBitmap, Size targetSize, int thumbnaillWidth, int thumbnaillHeight) private Image GetImageThumbnail(Image sourceBitmap, int thumbnaillWidth)
{ {
return sourceBitmap.Clone(x => x.BackgroundColor(Color.White).Resize(thumbnaillWidth, 0)); return sourceBitmap.Clone(x => x.BackgroundColor(Color.White).Resize(thumbnaillWidth, 0));
} }
} }

View File

@ -144,7 +144,7 @@ public class AuthenticationController : ControllerBase
} }
[AllowNotPayment] [AllowNotPayment]
[HttpPost("{code}", Order = int.MaxValue)] [HttpPost("{code}", Order = 1)]
public AuthenticationTokenDto AuthenticateMeFromBodyWithCode(AuthRequestsDto inDto) public AuthenticationTokenDto AuthenticateMeFromBodyWithCode(AuthRequestsDto inDto)
{ {
var tenant = _tenantManager.GetCurrentTenant().Id; var tenant = _tenantManager.GetCurrentTenant().Id;