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)
{
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 + ".{format}", defaults, policies), order - 1, 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, r.Metadata, r.DisplayName));
}
}).ToList();

View File

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

View File

@ -54,7 +54,12 @@ public class TempStream
public Stream Create()
{
return new FileStream(_tempPath.GetTempFileName(), FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose);
return Create(out _);
}
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.DependencyInjection;
global using Microsoft.Extensions.DependencyInjection.Extensions;
global using Microsoft.Extensions.Hosting;
global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Options;
global using Microsoft.Extensions.Primitives;

View File

@ -1,53 +1,53 @@
// (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.Core;
[Scope]
public interface IFileDao<T>
{
// (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.Core;
[Scope]
public interface IFileDao<T>
{
/// <summary>
/// Clear the application cache for the specific file
/// </summary>
Task InvalidateCacheAsync(T fileId);
/// </summary>
Task InvalidateCacheAsync(T fileId);
/// <summary>
/// Receive file
/// </summary>
/// <param name="fileId">file id</param>
/// <returns></returns>
Task<File<T>> GetFileAsync(T fileId);
/// <returns></returns>
Task<File<T>> GetFileAsync(T fileId);
/// <summary>
/// Receive file
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param>
/// <returns></returns>
Task<File<T>> GetFileAsync(T fileId, int fileVersion);
/// <returns></returns>
Task<File<T>> GetFileAsync(T fileId, int fileVersion);
/// <summary>
/// Receive file
/// </summary>
@ -55,29 +55,29 @@ public interface IFileDao<T>
/// <param name="title">file name</param>
/// <returns>
/// file
/// </returns>
Task<File<T>> GetFileAsync(T parentId, string title);
/// </returns>
Task<File<T>> GetFileAsync(T parentId, string title);
/// <summary>
/// Receive last file without forcesave
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion"></param>
/// <returns></returns>
Task<File<T>> GetFileStableAsync(T fileId, int fileVersion = -1);
Task<File<T>> GetFileStableAsync(T fileId, int fileVersion = -1);
/// <summary>
/// Returns all versions of the file
/// </summary>
/// <param name="fileId"></param>
/// <returns></returns>
IAsyncEnumerable<File<T>> GetFileHistoryAsync(T fileId);
/// <returns></returns>
IAsyncEnumerable<File<T>> GetFileHistoryAsync(T fileId);
/// <summary>
/// Gets the file (s) by ID (s)
/// </summary>
/// <param name="fileIds">id file</param>
/// <returns></returns>
IAsyncEnumerable<File<T>> GetFilesAsync(IEnumerable<T> fileIds);
/// <returns></returns>
IAsyncEnumerable<File<T>> GetFilesAsync(IEnumerable<T> fileIds);
/// <summary>
/// Gets the file (s) by ID (s) for share
/// </summary>
@ -88,15 +88,15 @@ public interface IFileDao<T>
/// <param name="searchText"></param>
/// <param name="searchInContent"></param>
/// <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>
/// <param name="parentId"></param>
/// <returns></returns>
Task<List<T>> GetFilesAsync(T parentId);
/// <returns></returns>
Task<List<T>> GetFilesAsync(T parentId);
/// <summary>
/// Get files in folder
/// </summary>
@ -111,39 +111,39 @@ public interface IFileDao<T>
/// <returns>list of files</returns>
/// <remarks>
/// Return only the latest versions of files of a folder
/// </remarks>
IAsyncEnumerable<File<T>> GetFilesAsync(T parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false);
/// </remarks>
IAsyncEnumerable<File<T>> GetFilesAsync(T parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false);
/// <summary>
/// Get stream of file
/// </summary>
/// <param name="file"></param>
/// <returns>Stream</returns>
Task<Stream> GetFileStreamAsync(File<T> file);
/// <returns>Stream</returns>
Task<Stream> GetFileStreamAsync(File<T> file);
/// <summary>
/// Get stream of file
/// </summary>
/// <param name="file"></param>
/// <param name="offset"></param>
/// <returns>Stream</returns>
Task<Stream> GetFileStreamAsync(File<T> file, long offset);
/// <returns>Stream</returns>
Task<Stream> GetFileStreamAsync(File<T> file, long offset);
/// <summary>
/// Get presigned uri
/// </summary>
/// <param name="file"></param>
/// <param name="expires"></param>
/// <returns>Stream uri</returns>
Task<Uri> GetPreSignedUriAsync(File<T> file, TimeSpan expires);
/// <returns>Stream uri</returns>
Task<Uri> GetPreSignedUriAsync(File<T> file, TimeSpan expires);
/// <summary>
/// Check is supported PreSignedUri
/// </summary>
/// <param name="file"></param>
/// <returns>Stream uri</returns>
Task<bool> IsSupportedPreSignedUriAsync(File<T> file);
/// <returns>Stream uri</returns>
Task<bool> IsSupportedPreSignedUriAsync(File<T> file);
/// <summary>
/// Saves / updates the version of the file
/// and save stream of file
@ -158,96 +158,96 @@ public interface IFileDao<T>
///
/// Save in all other cases
/// </remarks>
Task<File<T>> SaveFileAsync(File<T> file, Stream fileStream);
Task<File<T>> SaveFileAsync(File<T> file, Stream fileStream);
/// <summary>
///
/// </summary>
/// <param name="file"></param>
/// <param name="fileStream"></param>
/// <returns></returns>
Task<File<T>> ReplaceFileVersionAsync(File<T> file, Stream fileStream);
Task<File<T>> ReplaceFileVersionAsync(File<T> file, Stream fileStream);
/// <summary>
/// Deletes a file including all previous versions
/// </summary>
/// <param name="fileId">file id</param>
Task DeleteFileAsync(T fileId);
Task DeleteFileAsync(T fileId);
/// <summary>
/// Checks whether or not file
/// </summary>
/// <param name="title">file name</param>
/// <param name="folderId">folder id</param>
/// <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>
/// Moves a file or set of files in a folder
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="toFolderId">The ID of the destination folder</param>
Task<T> MoveFileAsync(T fileId, T toFolderId);
Task<TTo> MoveFileAsync<TTo>(T fileId, TTo toFolderId);
Task<string> MoveFileAsync(T fileId, string toFolderId);
Task<int> MoveFileAsync(T fileId, int toFolderId);
/// <param name="toFolderId">The ID of the destination folder</param>
Task<T> MoveFileAsync(T fileId, T toFolderId);
Task<TTo> MoveFileAsync<TTo>(T fileId, TTo toFolderId);
Task<string> MoveFileAsync(T fileId, string toFolderId);
Task<int> MoveFileAsync(T fileId, int toFolderId);
/// <summary>
/// Copy the files in a folder
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="toFolderId">The ID of the destination folder</param>
Task<File<T>> CopyFileAsync(T fileId, T toFolderId);
Task<File<TTo>> CopyFileAsync<TTo>(T fileId, TTo toFolderId);
Task<File<string>> CopyFileAsync(T fileId, string toFolderId);
Task<File<int>> CopyFileAsync(T fileId, int toFolderId);
/// <param name="toFolderId">The ID of the destination folder</param>
Task<File<T>> CopyFileAsync(T fileId, T toFolderId);
Task<File<TTo>> CopyFileAsync<TTo>(T fileId, TTo toFolderId);
Task<File<string>> CopyFileAsync(T fileId, string toFolderId);
Task<File<int>> CopyFileAsync(T fileId, int toFolderId);
/// <summary>
/// Rename file
/// </summary>
/// <param name="file"></param>
/// <param name="newTitle">new name</param>
Task<T> FileRenameAsync(File<T> file, string newTitle);
/// <param name="newTitle">new name</param>
Task<T> FileRenameAsync(File<T> file, string newTitle);
/// <summary>
/// Update comment file
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</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>
/// Complete file version
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param>
Task CompleteVersionAsync(T fileId, int fileVersion);
/// <param name="fileVersion">file version</param>
Task CompleteVersionAsync(T fileId, int fileVersion);
/// <summary>
/// Continue file version
/// </summary>
/// <param name="fileId">file id</param>
/// <param name="fileVersion">file version</param>
Task ContinueVersionAsync(T fileId, int fileVersion);
/// <param name="fileVersion">file version</param>
Task ContinueVersionAsync(T fileId, int fileVersion);
/// <summary>
/// Check the need to use the trash before removing
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
bool UseTrashForRemove(File<T> file);
string GetUniqFilePath(File<T> file, string fileTitle);
bool UseTrashForRemove(File<T> file);
string GetUniqFilePath(File<T> file, string fileTitle);
#region chunking
Task<ChunkedUploadSession<T>> CreateUploadSessionAsync(File<T> file, long contentLength);
Task<File<T>> UploadChunkAsync(ChunkedUploadSession<T> uploadSession, Stream chunkStream, long chunkLength);
Task AbortUploadSessionAsync(ChunkedUploadSession<T> uploadSession);
Task<ChunkedUploadSession<T>> CreateUploadSessionAsync(File<T> file, long contentLength);
Task<File<T>> UploadChunkAsync(ChunkedUploadSession<T> uploadSession, Stream chunkStream, long chunkLength);
Task AbortUploadSessionAsync(ChunkedUploadSession<T> uploadSession);
#endregion
#region Only in TMFileDao
/// <summary>
/// Set created by
/// </summary>
/// <param name="fileIds"></param>
/// <param name="newOwnerId"></param>
Task ReassignFilesAsync(T[] fileIds, Guid newOwnerId);
/// <param name="newOwnerId"></param>
Task ReassignFilesAsync(T[] fileIds, Guid newOwnerId);
/// <summary>
/// Search files in SharedWithMe & Projects
/// </summary>
@ -258,7 +258,7 @@ public interface IFileDao<T>
/// <param name="searchText"></param>
/// <param name="searchInContent"></param>
/// <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>
/// Search the list of files containing text
/// Only in TMFileDao
@ -266,34 +266,36 @@ public interface IFileDao<T>
/// <param name="text">search text</param>
/// <param name="bunch"></param>
/// <returns>list of files</returns>
IAsyncEnumerable<File<T>> SearchAsync(string text, bool bunch = false);
IAsyncEnumerable<File<T>> SearchAsync(string text, bool bunch = false);
/// <summary>
/// Checks whether file exists on storage
/// </summary>
/// <param name="file">file</param>
/// <returns></returns>
Task<bool> IsExistOnStorageAsync(File<T> file);
Task SaveEditHistoryAsync(File<T> file, string changes, Stream differenceStream);
Task<List<EditHistory>> GetEditHistoryAsync(DocumentServiceHelper documentServiceHelper, T fileId, int fileVersion = 0);
Task<Stream> GetDifferenceStreamAsync(File<T> file);
Task<bool> ContainChangesAsync(T fileId, int fileVersion);
Task SaveThumbnailAsync(File<T> file, Stream thumbnail, 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<IEnumerable<int>> GetTenantsWithFeedsAsync(DateTime fromTime);
Task<EntryProperties> GetProperties(T fileId);
Task SaveProperties(T fileId, EntryProperties entryProperties);
Task<bool> IsExistOnStorageAsync(File<T> file);
Task SaveEditHistoryAsync(File<T> file, string changes, Stream differenceStream);
Task<List<EditHistory>> GetEditHistoryAsync(DocumentServiceHelper documentServiceHelper, T fileId, int fileVersion = 0);
Task<Stream> GetDifferenceStreamAsync(File<T> file);
Task<bool> ContainChangesAsync(T fileId, int fileVersion);
Task SaveThumbnailAsync(File<T> file, Stream thumbnail, int width, int height);
Task<Stream> GetThumbnailAsync(File<T> file, int width, int height);
Task<Stream> GetThumbnailAsync(T fileId, int width, int height);
Task<IEnumerable<FileWithShare>> GetFeedsAsync(int tenant, DateTime from, DateTime to);
Task<IEnumerable<int>> GetTenantsWithFeedsAsync(DateTime fromTime);
Task<EntryProperties> GetProperties(T fileId);
Task SaveProperties(T fileId, EntryProperties entryProperties);
#endregion
}

View File

@ -1465,6 +1465,12 @@ internal class FileDao : AbstractDao, IFileDao<int>
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)
{
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.NativeAccessor = boxFile;
file.Title = MakeFileTitle(boxFile);
file.ThumbnailStatus = Thumbnail.Created;
return file;
}

View File

@ -550,6 +550,12 @@ internal class BoxFileDao : BoxDaoBase, IFileDao<string>
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
private File<string> RestoreIds(File<string> file)

View File

@ -147,6 +147,12 @@ internal class BoxProviderInfo : IProviderInfo
{
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]
@ -332,4 +338,9 @@ public class BoxProviderInfoHelper
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:
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.NativeAccessor = dropboxFile;
file.Title = MakeFileTitle(dropboxFile);
file.ThumbnailStatus = Thumbnail.Created;
return file;
}

View File

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

View File

@ -132,9 +132,15 @@ internal class DropboxProviderInfo : IProviderInfo
{
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
{
public DropboxStorage Storage { get; private set; }
@ -163,7 +169,7 @@ internal class DropboxStorageDisposableWrapper : IDisposable
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);
@ -311,4 +317,17 @@ public class DropboxProviderInfoHelper
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
// 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;
[Scope]
internal class DropboxStorage : IDisposable
{
public bool IsOpened { get; private set; }
@ -135,10 +138,38 @@ internal class DropboxStorage : IDisposable
public async Task<List<Metadata>> GetItemsAsync(string folderPath)
{
var data = await _dropboxClient.Files.ListFolderAsync(folderPath);
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)
{
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.NativeAccessor = driveFile;
file.Title = MakeFileTitle(driveFile);
file.ThumbnailStatus = Thumbnail.Created;
return file;
}

View File

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

View File

@ -158,6 +158,12 @@ internal class GoogleDriveProviderInfo : IProviderInfo
{
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))]

View File

@ -155,7 +155,6 @@ internal class GoogleDriveStorage : IDisposable
try
{
var request = _driveService.Files.Get(entryId);
request.Fields = GoogleLoginProvider.FilesFields;
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)
{
var request = _driveService.Files.List();

View File

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

View File

@ -561,6 +561,12 @@ internal class OneDriveFileDao : OneDriveDaoBase, IFileDao<string>
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
private File<string> RestoreIds(File<string> file)

View File

@ -123,6 +123,12 @@ internal class OneDriveProviderInfo : IProviderInfo
{
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))]
@ -284,6 +290,11 @@ public class OneDriveProviderInfoHelper
_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
{

View File

@ -354,6 +354,30 @@ internal class OneDriveStorage
var httpClient = _clientFactory.CreateClient();
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 fileDao = selector.GetFileDao(file.Id);
return fileDao.UseTrashForRemove(file);
return fileDao.UseTrashForRemove(file);
}
#region chunking
@ -520,5 +520,19 @@ internal class ProviderFileDao : ProviderDaoBase, IFileDao<string>
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
}

View File

@ -65,7 +65,7 @@ public class ThumbnailSettings
private 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;
}

View File

@ -27,18 +27,18 @@
using JWT.Algorithms;
using JWT.Builder;
using JsonException = System.Text.Json.JsonException;
using MimeMapping = ASC.Common.Web.MimeMapping;
namespace ASC.Web.Files;
using JsonException = System.Text.Json.JsonException;
using MimeMapping = ASC.Common.Web.MimeMapping;
namespace ASC.Web.Files;
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);
}
@ -47,7 +47,7 @@ public class FileHandler
[Scope]
public class FileHandlerService
{
private readonly ThumbnailSettings _thumbnailSettings;
private readonly ThumbnailSettings _thumbnailSettings;
public string FileHandlerPath
{
@ -109,10 +109,10 @@ public class FileHandlerService
IServiceProvider serviceProvider,
TempStream tempStream,
SocketManager socketManager,
CompressToArchive compressToArchive,
InstanceCrypto instanceCrypto,
IHttpClientFactory clientFactory,
ThumbnailSettings thumbnailSettings)
CompressToArchive compressToArchive,
InstanceCrypto instanceCrypto,
IHttpClientFactory clientFactory,
ThumbnailSettings thumbnailSettings)
{
_filesLinkUtility = filesLinkUtility;
_tenantExtra = tenantExtra;
@ -142,7 +142,7 @@ public class FileHandlerService
_userManager = userManager;
_logger = logger;
_clientFactory = clientFactory;
this._thumbnailSettings = thumbnailSettings;
this._thumbnailSettings = thumbnailSettings;
}
public Task Invoke(HttpContext context)
@ -225,7 +225,7 @@ public class FileHandlerService
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();
if (!await store.IsFileAsync(FileConstant.StorageDomainTmp, path))
@ -336,7 +336,7 @@ public class FileHandlerService
if (!await fileDao.IsExistOnStorageAsync(file))
{
_logger.ErrorDownloadFile2(file.Id.ToString());
_logger.ErrorDownloadFile2(file.Id.ToString());
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
return;
@ -412,7 +412,7 @@ public class FileHandlerService
{
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);
await store.SaveAsync(string.Empty, mp4Path, stream, mp4Name);
}
@ -1009,34 +1009,34 @@ public class FileHandlerService
}
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
{
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);
}
var fileDao = _daoFactory.GetFileDao<T>();
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);
}
var fileDao = _daoFactory.GetFileDao<int>();
var file = int.TryParse(context.Request.Query[FilesLinkUtility.Version], out var version) && version > 0
? await fileDao.GetFileAsync(id, version)
: await fileDao.GetFileAsync(id);
@ -1069,7 +1069,7 @@ public class FileHandlerService
context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue("." + _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));
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)
{
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)
@ -1194,33 +1253,33 @@ public class FileHandlerService
context.Response.Redirect(
(context.Request.Query["openfolder"].FirstOrDefault() ?? "").Equals("true")
? await _pathProvider.GetFolderUrlByIdAsync(file.ParentId)
: (_filesLinkUtility.GetFileWebEditorUrl(file.Id) + "#message/" + HttpUtility.UrlEncode(string.Format(FilesCommonResource.MessageFileCreated, folder.Title))));
? await _pathProvider.GetFolderUrlByIdAsync(file.ParentId)
: (_filesLinkUtility.GetFileWebEditorUrl(file.Id) + "#message/" + HttpUtility.UrlEncode(string.Format(FilesCommonResource.MessageFileCreated, folder.Title))));
}
private async Task InternalWriteError(HttpContext context, Exception ex, bool responseMessage)
{
_logger.ErrorFileHandler(ex);
if (responseMessage)
{
await context.Response.WriteAsync("error: " + ex.Message);
return;
}
context.Response.Redirect(PathProvider.StartURL + "#error/" + HttpUtility.UrlEncode(ex.Message), true);
return;
}
private Task InternalWriteOk<T>(HttpContext context, Folder<T> folder, File<T> file)
{
var message = string.Format(FilesCommonResource.MessageFileCreated, folder.Title);
if (_fileUtility.CanWebRestrictedEditing(file.Title))
{
message = string.Format(FilesCommonResource.MessageFileCreatedForm, folder.Title);
}
return context.Response.WriteAsync("ok: " + message);
}
_logger.ErrorFileHandler(ex);
if (responseMessage)
{
await context.Response.WriteAsync("error: " + ex.Message);
return;
}
context.Response.Redirect(PathProvider.StartURL + "#error/" + HttpUtility.UrlEncode(ex.Message), true);
return;
}
private Task InternalWriteOk<T>(HttpContext context, Folder<T> folder, File<T> file)
{
var message = string.Format(FilesCommonResource.MessageFileCreated, folder.Title);
if (_fileUtility.CanWebRestrictedEditing(file.Title))
{
message = string.Format(FilesCommonResource.MessageFileCreatedForm, folder.Title);
}
return context.Response.WriteAsync("ok: " + message);
}
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>>();
file.Title = fileTitle;
file.ParentId = folder.Id;
file.ParentId = folder.Id;
file.Comment = FilesCommonResource.CommentCreate;
var fileDao = _daoFactory.GetFileDao<T>();
@ -1278,7 +1337,7 @@ public class FileHandlerService
var file = _serviceProvider.GetService<File<T>>();
file.Title = fileTitle;
file.ParentId = folder.Id;
file.ParentId = folder.Id;
file.Comment = FilesCommonResource.CommentCreate;
var request = new HttpRequestMessage
@ -1351,7 +1410,7 @@ public class FileHandlerService
return;
}
urlRedirect = _filesLinkUtility.GetFileWebPreviewUrl(_fileUtility, file.Title, file.Id);
urlRedirect = _filesLinkUtility.GetFileWebPreviewUrl(_fileUtility, file.Title, file.Id);
}
if (string.IsNullOrEmpty(urlRedirect))
@ -1515,4 +1574,4 @@ public static class FileHandlerExtensions
{
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")]
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);
}

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)
{
return MustConvertable.Contains(extension.TrimStart('.'));
}
public bool ExistFormat(string extension)
{
return _fFmpegFormats.Contains(extension);
}
public Task<Stream> Convert(Stream inputStream, string inputFormat)
{
if (inputStream == null)
@ -68,17 +82,13 @@ public class FFmpegService
{
var startInfo = PrepareFFmpeg(inputFormat);
Process process;
using (process = new Process { StartInfo = startInfo })
{
process.Start();
using var process = Process.Start(startInfo);
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)
@ -86,6 +96,8 @@ public class FFmpegService
_logger = logger;
_fFmpegPath = configuration["files:ffmpeg:value"];
_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();
@ -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)
{
if (!_convertableMedia.Contains(inputFormat.TrimStart('.')))
@ -134,6 +139,15 @@ public class FFmpegService
throw new ArgumentException("input format");
}
var startInfo = PrepareCommonFFmpeg();
startInfo.Arguments = string.Format(_fFmpegArgs, "mp4");
return startInfo;
}
private ProcessStartInfo PrepareCommonFFmpeg()
{
var startInfo = new ProcessStartInfo();
if (string.IsNullOrEmpty(_fFmpegPath))
@ -143,56 +157,15 @@ public class FFmpegService
}
startInfo.FileName = _fFmpegPath;
startInfo.WorkingDirectory = Path.GetDirectoryName(_fFmpegPath);
startInfo.Arguments = string.Format(_fFmpegArgs, "mp4");
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.RedirectStandardError = true;
startInfo.CreateNoWindow = true;
startInfo.WindowStyle = ProcessWindowStyle.Normal;
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)
{
using var reader = new StreamReader(stream, Encoding.UTF8);
@ -202,4 +175,15 @@ public class FFmpegService
_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;
}
[HttpGet("file/app-{fileId}", Order = int.MaxValue)]
[HttpGet("file/app-{fileId}", Order = 1)]
public async Task<FileEntryDto> GetFileInfoThirdPartyAsync(string 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>
/// <category>Uploads</category>
/// <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)
{
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="keepConvertStatus" visible="false">Keep status conversation after finishing</param>
/// <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)
{
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
// 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.Linq.Expressions;
global using System.Reflection;
global using System.Linq.Expressions;
global using System.Reflection;
global using System.Text.Json;
global using ASC.Api.Core;
global using ASC.Api.Core.Extensions;
global using ASC.Common;
global using ASC.Common.Caching;
global using ASC.Common.DependencyInjection;
global using ASC.Api.Core.Extensions;
global using ASC.Common;
global using ASC.Common.Caching;
global using ASC.Common.DependencyInjection;
global using ASC.Common.Log;
global using ASC.Common.Mapping;
global using ASC.Common.Utils;
global using ASC.Core;
global using ASC.Core.Common;
global using ASC.Core.Common.EF;
global using ASC.Core.Common.Hosting;
global using ASC.Core.Common.Hosting.Interfaces;
global using ASC.Core.Tenants;
global using ASC.ElasticSearch;
global using ASC.ElasticSearch.Service;
global using ASC.Common.Mapping;
global using ASC.Core;
global using ASC.Core.ChunkedUploader;
global using ASC.Core.Common;
global using ASC.Core.Common.EF;
global using ASC.Core.Common.Hosting;
global using ASC.Core.Common.Hosting.Interfaces;
global using ASC.Core.Tenants;
global using ASC.ElasticSearch;
global using ASC.ElasticSearch.Service;
global using ASC.EventBus.Abstractions;
global using ASC.EventBus.Log;
global using ASC.Feed;
global using ASC.Feed.Aggregator.Service;
global using ASC.Feed;
global using ASC.Feed.Aggregator.Service;
global using ASC.Feed.Core;
global using ASC.Feed.Data;
global using ASC.Feed.Data;
global using ASC.Files.AutoCleanUp;
global using ASC.Files.Core;
global using ASC.Files.Core.Core.Entries;
global using ASC.Files.Core.EF;
global using ASC.Files.Core;
global using ASC.Files.Core.Core.Entries;
global using ASC.Files.Core.EF;
global using ASC.Files.Core.IntegrationEvents.Events;
global using ASC.Files.Core.Log;
global using ASC.Files.Core.Resources;
global using ASC.Files.Core.Security;
global using ASC.Files.Core.Log;
global using ASC.Files.Core.Resources;
global using ASC.Files.Core.Security;
global using ASC.Files.Expired;
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.Web.Core;
global using ASC.Web.Core.Files;
global using ASC.Web.Core.Users;
global using ASC.Web.Files.Classes;
global using ASC.Web.Files.Core;
global using ASC.Web.Files.Core.Search;
global using ASC.Web.Files.Services.DocumentService;
global using ASC.Web.Files.Utils;
global using ASC.Web.Studio.Core;
global using ASC.Web.Core;
global using ASC.Web.Core.Files;
global using ASC.Web.Files.Classes;
global using ASC.Web.Files.Core.Search;
global using ASC.Web.Files.Services.DocumentService;
global using ASC.Web.Files.Services.FFmpegService;
global using ASC.Web.Files.Utils;
global using ASC.Web.Studio.Core;
global using Autofac;
global using Autofac;
global using Microsoft.AspNetCore.Builder;
global using Microsoft.Extensions.Hosting.WindowsServices;
global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Hosting.WindowsServices;
global using Microsoft.Extensions.Logging;
global using SixLabors.ImageSharp;
global using SixLabors.ImageSharp.Formats.Png;
global using SixLabors.ImageSharp;
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>();
diHelper.TryAdd<Launcher>();
services.AddHostedService<DeleteExpiredService>();
diHelper.TryAdd<DeleteExpiredService>();
diHelper.TryAdd<AuthManager>();
diHelper.TryAdd<BaseCommonLinkUtility>();
diHelper.TryAdd<FeedAggregateDataProvider>();

View File

@ -42,7 +42,7 @@ public class BuilderQueue<T>
_config = settings;
}
public async Task BuildThumbnails(IEnumerable<FileData<T>> filesWithoutThumbnails)
public async Task BuildThumbnails(IEnumerable<FileData<T>> filesWithoutThumbnails)
{
try
{
@ -70,18 +70,25 @@ public class BuilderQueue<T>
[Scope]
public class Builder<T>
{
private readonly ThumbnailSettings _config;
private readonly ILogger _logger;
private readonly TenantManager _tenantManager;
private readonly IDaoFactory _daoFactory;
private readonly DocumentServiceConnector _documentServiceConnector;
private readonly DocumentServiceHelper _documentServiceHelper;
private readonly Global _global;
private readonly PathProvider _pathProvider;
private readonly ThumbnailSettings _config;
private readonly ILogger _logger;
private readonly TenantManager _tenantManager;
private readonly IDaoFactory _daoFactory;
private readonly DocumentServiceConnector _documentServiceConnector;
private readonly DocumentServiceHelper _documentServiceHelper;
private readonly Global _global;
private readonly PathProvider _pathProvider;
private readonly IHttpClientFactory _clientFactory;
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(
ThumbnailSettings settings,
TenantManager tenantManager,
@ -92,8 +99,10 @@ public class Builder<T>
PathProvider pathProvider,
ILoggerProvider log,
IHttpClientFactory clientFactory,
FFmpegService fFmpegService,
TempPath tempPath,
SocketManager socketManager,
ThumbnailSettings thumbnailSettings)
ThumbnailSettings thumbnailSettings)
{
_config = settings;
_tenantManager = tenantManager;
@ -104,11 +113,13 @@ public class Builder<T>
_pathProvider = pathProvider;
_logger = log.CreateLogger("ASC.Files.ThumbnailBuilder");
_clientFactory = clientFactory;
_fFmpegService = fFmpegService;
_tempPath = tempPath;
_socketManager = socketManager;
_thumbnailSettings = thumbnailSettings;
}
internal async Task BuildThumbnail(FileData<T> fileData)
internal async Task BuildThumbnail(FileData<T> fileData)
{
try
{
@ -122,7 +133,7 @@ public class Builder<T>
return;
}
await GenerateThumbnail(fileDao, fileData);
await GenerateThumbnail(fileDao, fileData);
}
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;
try
{
file = await fileDao.GetFileAsync(fileData.FileId);
file = await fileDao.GetFileAsync(fileData.FileId);
if (file == null)
{
@ -158,29 +169,37 @@ public class Builder<T>
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;
foreach (var size in _thumbnailSettings.Sizes)
{
await fileDao.SaveThumbnailAsync(file, null, size.Width, size.Height);
}
}
return;
}
if (IsImage(file))
if (IsVideo(ext))
{
await CropImage(fileDao, file);
await MakeThumbnailFromVideo(fileDao, file);
}
else
{
await MakeThumbnail(fileDao, file);
if (IsImage(ext))
{
await CropImage(fileDao, file);
}
else
{
await MakeThumbnail(fileDao, file);
}
}
var newFile = await fileDao.GetFileStableAsync(file.Id);
await _socketManager.UpdateFileAsync(newFile);
await _socketManager.UpdateFileAsync(newFile);
}
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)
{
@ -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);
fileUri = _documentServiceConnector.ReplaceCommunityAdress(fileUri);
@ -273,7 +315,7 @@ public class Builder<T>
IgnorePrintArea = true,
//Orientation = "landscape", // "297mm" x "210mm"
FitToHeight = height,
FitToWidth = width,
FitToWidth = width,
Headings = false,
GridLines = false,
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);
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);
using var request = new HttpRequestMessage();
using var request = new HttpRequestMessage();
request.RequestUri = new Uri(thumbnailUrl);
var httpClient = _clientFactory.CreateClient();
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))
{
await CropAsync(sourceImg, fileDao, file, width, height);
}
}
}
_logger.DebugMakeThumbnail4(file.Id.ToString());
}
private bool IsImage(File<T> file)
private bool CanCreateThumbnail(string extention)
{
var extension = FileUtility.GetFileExtension(file.Title);
return FileUtility.ExtsImage.Contains(extension);
return _config.FormatsArray.Contains(extention) || IsVideo(extention) || IsImage(extention);
}
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());
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());
}
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>();
@ -357,12 +407,11 @@ public class Builder<T>
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, targetSize, width, height);
using var targetImg = GetImageThumbnail(sourceImg, width);
using var targetStream = new MemoryStream();
switch (_global.ThumbnailExtension)
switch (_global.ThumbnailExtension)
{
case ThumbnailExtension.bmp:
await targetImg.SaveAsBmpAsync(targetStream);
@ -392,8 +441,8 @@ public class Builder<T>
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));
}
}
}

View File

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