Merge branch 'develop' into feature/integration-system
This commit is contained in:
commit
9d8a3c0104
@ -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();
|
||||||
|
@ -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))
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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;
|
||||||
|
@ -287,6 +287,8 @@ public interface IFileDao<T>
|
|||||||
|
|
||||||
Task<Stream> GetThumbnailAsync(File<T> file, 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<FileWithShare>> GetFeedsAsync(int tenant, DateTime from, DateTime to);
|
||||||
|
|
||||||
Task<IEnumerable<int>> GetTenantsWithFeedsAsync(DateTime fromTime);
|
Task<IEnumerable<int>> GetTenantsWithFeedsAsync(DateTime fromTime);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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>();
|
||||||
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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))]
|
||||||
|
@ -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();
|
||||||
|
@ -77,7 +77,12 @@ internal abstract class ThirdPartyProviderDao
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1009,11 +1009,11 @@ 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
|
||||||
{
|
{
|
||||||
@ -1036,7 +1036,7 @@ public class FileHandlerService
|
|||||||
_ = 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);
|
||||||
@ -1101,6 +1101,65 @@ 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;
|
||||||
|
@ -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.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}")]
|
[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);
|
||||||
}
|
}
|
||||||
|
@ -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,24 +82,22 @@ 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)
|
||||||
{
|
{
|
||||||
_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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
54
products/ASC.Files/Service/Expired/DeleteExpiredService.cs
Normal file
54
products/ASC.Files/Service/Expired/DeleteExpiredService.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,8 +37,8 @@ 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;
|
||||||
@ -60,16 +60,16 @@ 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;
|
|
||||||
global using ASC.Web.Files.Core.Search;
|
global using ASC.Web.Files.Core.Search;
|
||||||
global using ASC.Web.Files.Services.DocumentService;
|
global using ASC.Web.Files.Services.DocumentService;
|
||||||
|
global using ASC.Web.Files.Services.FFmpegService;
|
||||||
global using ASC.Web.Files.Utils;
|
global using ASC.Web.Files.Utils;
|
||||||
global using ASC.Web.Studio.Core;
|
global using ASC.Web.Studio.Core;
|
||||||
|
|
||||||
@ -80,6 +80,5 @@ 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;
|
@ -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>();
|
||||||
|
@ -81,6 +81,13 @@ public class Builder<T>
|
|||||||
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,
|
||||||
@ -92,6 +99,8 @@ 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)
|
||||||
{
|
{
|
||||||
@ -104,6 +113,8 @@ 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;
|
||||||
}
|
}
|
||||||
@ -158,7 +169,7 @@ 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)
|
||||||
@ -169,7 +180,14 @@ public class Builder<T>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsImage(file))
|
if (IsVideo(ext))
|
||||||
|
{
|
||||||
|
await MakeThumbnailFromVideo(fileDao, file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
if (IsImage(ext))
|
||||||
{
|
{
|
||||||
await CropImage(fileDao, file);
|
await CropImage(fileDao, file);
|
||||||
}
|
}
|
||||||
@ -177,6 +195,7 @@ public class Builder<T>
|
|||||||
{
|
{
|
||||||
await MakeThumbnail(fileDao, file);
|
await MakeThumbnail(fileDao, file);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var newFile = await fileDao.GetFileStableAsync(file.Id);
|
var newFile = await fileDao.GetFileStableAsync(file.Id);
|
||||||
|
|
||||||
@ -196,6 +215,29 @@ public class Builder<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
private async Task MakeThumbnail(IFileDao<T> fileDao, File<T> file)
|
||||||
{
|
{
|
||||||
foreach (var w in _config.Sizes)
|
foreach (var w in _config.Sizes)
|
||||||
@ -314,11 +356,19 @@ public class Builder<T>
|
|||||||
_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 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)
|
private async Task CropImage(IFileDao<T> fileDao, File<T> file)
|
||||||
@ -359,8 +409,7 @@ public class Builder<T>
|
|||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
@ -392,7 +441,7 @@ 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));
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user