From 14eb6837c8139013e42b2c7589de17c5b1453c30 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Mon, 11 Oct 2021 10:03:04 +0800 Subject: [PATCH 1/2] fix bug(Oss-FileSystem): turning a page to query an array may cause an out-of-bounds subscript --- .../FileSystem/FileSystemOssContainer.cs | 1082 +++++++++-------- 1 file changed, 542 insertions(+), 540 deletions(-) diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.FileSystem/LINGYUN/Abp/OssManagement/FileSystem/FileSystemOssContainer.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.FileSystem/LINGYUN/Abp/OssManagement/FileSystem/FileSystemOssContainer.cs index 1fb999e2e..1edab3676 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.FileSystem/LINGYUN/Abp/OssManagement/FileSystem/FileSystemOssContainer.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.FileSystem/LINGYUN/Abp/OssManagement/FileSystem/FileSystemOssContainer.cs @@ -1,540 +1,542 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Volo.Abp; -using Volo.Abp.BlobStoring; -using Volo.Abp.BlobStoring.FileSystem; -using Volo.Abp.IO; -using Volo.Abp.MultiTenancy; - -namespace LINGYUN.Abp.OssManagement.FileSystem -{ - /// - /// Oss容器的本地文件系统实现 - /// - internal class FileSystemOssContainer : IOssContainer - { - protected ICurrentTenant CurrentTenant { get; } - protected IHostEnvironment Environment { get; } - protected IBlobFilePathCalculator FilePathCalculator { get; } - protected IBlobContainerConfigurationProvider ConfigurationProvider { get; } - protected IServiceProvider ServiceProvider { get; } - protected FileSystemOssOptions Options { get; } - - public FileSystemOssContainer( - ICurrentTenant currentTenant, - IHostEnvironment environment, - IServiceProvider serviceProvider, - IBlobFilePathCalculator blobFilePathCalculator, - IBlobContainerConfigurationProvider configurationProvider, - IOptions options) - { - CurrentTenant = currentTenant; - Environment = environment; - ServiceProvider = serviceProvider; - FilePathCalculator = blobFilePathCalculator; - ConfigurationProvider = configurationProvider; - Options = options.Value; - } - - public virtual Task BulkDeleteObjectsAsync(BulkDeleteObjectRequest request) - { - var objectPath = !request.Path.IsNullOrWhiteSpace() - ? request.Path.EnsureEndsWith('/') - : ""; - var filesPath = request.Objects.Select(x => CalculateFilePath(request.Bucket, objectPath + x)); - - foreach (var file in filesPath) - { - if (Directory.Exists(file)) - { - if (Directory.GetFileSystemEntries(file).Length > 0) - { - throw new BusinessException(code: OssManagementErrorCodes.ContainerDeleteWithNotEmpty); - // throw new ContainerDeleteWithNotEmptyException("00101", $"Can't not delete container {name}, because it is not empty!"); - } - Directory.Delete(file); - } - else if (File.Exists(file)) - { - File.Delete(file); - } - } - - return Task.CompletedTask; - } - - public virtual Task CreateAsync(string name) - { - var filePath = CalculateFilePath(name); - ThrowOfPathHasTooLong(filePath); - if (!Directory.Exists(filePath)) - { - Directory.CreateDirectory(filePath); - } - - var directoryInfo = new DirectoryInfo(filePath); - var container = new OssContainer( - directoryInfo.Name, - directoryInfo.CreationTime, - 0L, - directoryInfo.LastWriteTime, - new Dictionary - { - { "LastAccessTime", directoryInfo.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } - }); - - return Task.FromResult(container); - } - - public virtual async Task CreateObjectAsync(CreateOssObjectRequest request) - { - var objectPath = !request.Path.IsNullOrWhiteSpace() - ? request.Path.EnsureEndsWith('/') - : ""; - var objectName = objectPath.IsNullOrWhiteSpace() - ? request.Object - : objectPath + request.Object; - - var filePath = CalculateFilePath(request.Bucket, objectName); - if (!request.Content.IsNullOrEmpty()) - { - ThrowOfPathHasTooLong(filePath); - - FileMode fileMode = request.Overwrite ? FileMode.Create : FileMode.CreateNew; - if (!request.Overwrite && File.Exists(filePath)) - { - throw new BusinessException(code: OssManagementErrorCodes.ObjectAlreadyExists); - // throw new OssObjectAlreadyExistsException($"Can't not put object {objectName} in container {request.Bucket}, Because a file with the same name already exists in the directory!"); - } - - DirectoryHelper.CreateIfNotExists(Path.GetDirectoryName(filePath)); - - using (var fileStream = File.Open(filePath, fileMode, FileAccess.Write)) - { - await request.Content.CopyToAsync(fileStream); - - await fileStream.FlushAsync(); - } - var fileInfo = new FileInfo(filePath); - var ossObject = new OssObject( - fileInfo.Name, - objectPath, - fileInfo.CreationTime, - fileInfo.Length, - fileInfo.LastWriteTime, - new Dictionary - { - { "IsReadOnly", fileInfo.IsReadOnly.ToString() }, - { "LastAccessTime", fileInfo.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } - }) - { - FullName = fileInfo.FullName.Replace(Environment.ContentRootPath, "") - }; - ossObject.SetContent(request.Content); - - return ossObject; - } - else - { - ThrowOfPathHasTooLong(filePath); - if (Directory.Exists(filePath)) - { - throw new BusinessException(code: OssManagementErrorCodes.ObjectAlreadyExists); - // throw new OssObjectAlreadyExistsException($"Can't not put object {objectName} in container {request.Bucket}, Because a file with the same name already exists in the directory!"); - } - Directory.CreateDirectory(filePath); - var directoryInfo = new DirectoryInfo(filePath); - - var ossObject = new OssObject( - directoryInfo.Name.EnsureEndsWith('/'), - objectPath, - directoryInfo.CreationTime, - 0L, - directoryInfo.LastWriteTime, - new Dictionary - { - { "LastAccessTime", directoryInfo.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } - }, - true) - { - FullName = directoryInfo.FullName.Replace(Environment.ContentRootPath, "") - }; - ossObject.SetContent(request.Content); - - return ossObject; - } - } - - public virtual Task DeleteAsync(string name) - { - var filePath = CalculateFilePath(name); - if (!Directory.Exists(filePath)) - { - throw new BusinessException(code: OssManagementErrorCodes.ContainerNotFound); - } - // 非空目录无法删除 - if (Directory.GetFileSystemEntries(filePath).Length > 0) - { - throw new BusinessException(code: OssManagementErrorCodes.ContainerDeleteWithNotEmpty); - // throw new ContainerDeleteWithNotEmptyException("00101", $"Can't not delete container {name}, because it is not empty!"); - } - Directory.Delete(filePath); - - return Task.CompletedTask; - } - - public virtual Task DeleteObjectAsync(GetOssObjectRequest request) - { - var objectName = request.Path.IsNullOrWhiteSpace() - ? request.Object - : request.Path.EnsureEndsWith('/') + request.Object; - var filePath = CalculateFilePath(request.Bucket, objectName); - if (File.Exists(filePath)) - { - File.Delete(filePath); - } - else if (Directory.Exists(filePath)) - { - if (Directory.GetFileSystemEntries(filePath).Length > 0) - { - throw new BusinessException(code: OssManagementErrorCodes.ObjectDeleteWithNotEmpty); - } - Directory.Delete(filePath); - } - - return Task.CompletedTask; - } - - public virtual Task ExistsAsync(string name) - { - var filePath = CalculateFilePath(name); - - return Task.FromResult(Directory.Exists(filePath)); - } - - public virtual Task GetAsync(string name) - { - var filePath = CalculateFilePath(name); - if (!Directory.Exists(filePath)) - { - throw new BusinessException(code: OssManagementErrorCodes.ContainerNotFound); - // throw new ContainerNotFoundException($"Can't not found container {name} in file system"); - } - - var directoryInfo = new DirectoryInfo(filePath); - var container = new OssContainer( - directoryInfo.Name, - directoryInfo.CreationTime, - 0L, - directoryInfo.LastWriteTime, - new Dictionary - { - { "LastAccessTime", directoryInfo.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } - }); - - return Task.FromResult(container); - } - - public virtual async Task GetObjectAsync(GetOssObjectRequest request) - { - var objectPath = !request.Path.IsNullOrWhiteSpace() - ? request.Path.EnsureEndsWith('/') - : ""; - var objectName = objectPath.IsNullOrWhiteSpace() - ? request.Object - : objectPath + request.Object; - - var filePath = CalculateFilePath(request.Bucket, objectName); - if (!File.Exists(filePath)) - { - if (!Directory.Exists(filePath)) - { - throw new BusinessException(code: OssManagementErrorCodes.ObjectNotFound); - // throw new ContainerNotFoundException($"Can't not found object {objectName} in container {request.Bucket} with file system"); - } - var directoryInfo = new DirectoryInfo(filePath); - var ossObject = new OssObject( - directoryInfo.Name.EnsureEndsWith('/'), - objectPath, - directoryInfo.CreationTime, - 0L, - directoryInfo.LastWriteTime, - new Dictionary - { - { "LastAccessTime", directoryInfo.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } - }, - true) - { - FullName = directoryInfo.FullName.Replace(Environment.ContentRootPath, "") - }; - return ossObject; - } - else - { - var fileInfo = new FileInfo(filePath); - var ossObject = new OssObject( - fileInfo.Name, - objectPath, - fileInfo.CreationTime, - fileInfo.Length, - fileInfo.LastWriteTime, - new Dictionary - { - { "IsReadOnly", fileInfo.IsReadOnly.ToString() }, - { "LastAccessTime", fileInfo.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } - }) - { - FullName = fileInfo.FullName.Replace(Environment.ContentRootPath, "") - }; - using (var fileStream = File.OpenRead(filePath)) - { - var memoryStream = new MemoryStream(); - await fileStream.CopyToAsync(memoryStream); - ossObject.SetContent(memoryStream); - - if (!request.Process.IsNullOrWhiteSpace()) - { - using var serviceScope = ServiceProvider.CreateScope(); - var context = new FileSystemOssObjectContext(request.Process, ossObject, serviceScope.ServiceProvider); - foreach (var processer in Options.Processers) - { - await processer.ProcessAsync(context); - - if (context.Handled) - { - ossObject.SetContent(context.Content); - break; - } - } - } - } - - return ossObject; - } - } - - public virtual Task GetListAsync(GetOssContainersRequest request) - { - // 不传递Bucket 检索根目录的Bucket - var filePath = CalculateFilePath(null); - - // 获取根目录 - var directories = Directory.GetDirectories(filePath, request.Prefix ?? "*.*"); - - // 排序目录 - Array.Sort(directories, delegate (string x, string y) - { - return x.CompareTo(y); - }); - - var spiltDirectories = directories; - // 计算标记的位置进行截断 - if (!request.Marker.IsNullOrWhiteSpace()) - { - var markIndex = directories.FindIndex(x => x.EndsWith(request.Marker)); - if (markIndex < 0) - { - directories = new string[0]; - } - else - { - var markDirectories = new string[directories.Length - markIndex]; - Array.Copy(directories, markIndex, markDirectories, 0, markDirectories.Length); - directories = markDirectories; - } - } - // 需要截断最大的容器集合 - if (request.MaxKeys.HasValue) - { - spiltDirectories = directories.Take(request.MaxKeys ?? directories.Length).ToArray(); - } - var nextDirectory = spiltDirectories.Length < directories.Length ? directories[spiltDirectories.Length] : ""; - if (!nextDirectory.IsNullOrWhiteSpace()) - { - // 下一个标记的目录名称 - - nextDirectory = new DirectoryInfo(nextDirectory).Name; - } - // 容器对应的目录信息集合 - var directoryInfos = spiltDirectories.Select(x => new DirectoryInfo(x)); - // 返回Oss容器描述集合 - var response = new GetOssContainersResponse( - request.Prefix, - request.Marker, - nextDirectory, - directories.Length, - directoryInfos.Select(x => new OssContainer( - x.Name, - x.CreationTime, - 0L, - x.LastWriteTime, - new Dictionary - { - { "LastAccessTime", x.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } - })) - .ToList()); - - return Task.FromResult(response); - } - - public virtual Task GetObjectsAsync(GetOssObjectsRequest request) - { - // 先定位检索的目录 - var filePath = CalculateFilePath(request.BucketName, request.Prefix); - if (!Directory.Exists(filePath)) - { - throw new BusinessException(code: OssManagementErrorCodes.ContainerNotFound); - // throw new ContainerNotFoundException($"Can't not found container {request.BucketName} in file system"); - } - // 目录也属于Oss对象,需要抽象的文件系统集合来存储 - var fileSystemNames = Directory.GetFileSystemEntries(filePath); - int maxFilesCount = fileSystemNames.Length; - - // 排序所有文件与目录 - Array.Sort(fileSystemNames, delegate (string x, string y) - { - // 检索的是文件系统名称 - // 需要判断是否为文件夹进行升序排序 - // 参考 OssObjectComparer - - var xFolder = Directory.Exists(x); - var yFolder = Directory.Exists(y); - - if (xFolder && yFolder) - { - return x.CompareTo(y); - } - - if (xFolder && !yFolder) - { - return -1; - } - - if (!xFolder && yFolder) - { - return 1; - } - - return x.CompareTo(y); - }); - - // 需要计算从哪个位置截断 - int markIndex = 0; - if (!request.Marker.IsNullOrWhiteSpace()) - { - markIndex = fileSystemNames.FindIndex(x => x.EndsWith(request.Marker)); - if (markIndex < 0) - { - markIndex = 0; - } - } - - // 需要截断Oss对象列表 - var copyFileSystemNames = fileSystemNames; - if (markIndex > 0) - { - copyFileSystemNames = fileSystemNames[(markIndex+1)..]; - } - // 截取指定数量的Oss对象 - int maxResultCount = request.MaxKeys ?? 10; - // Oss对象信息集合 - var fileSystems = copyFileSystemNames - .Take(maxResultCount) - .Select(file => - { - if (File.Exists(file)) - { - return new FileInfo(file); - } - return new DirectoryInfo(file); - }) - .ToArray(); - - // 计算下一页起始标记文件/目录名称 - var nextMarkerIndex = fileSystemNames.FindIndex(x => x.EndsWith(fileSystems[fileSystems.Length - 1].Name)); - string nextMarker = ""; - if (nextMarkerIndex >=0 && nextMarkerIndex + 1 < fileSystemNames.Length) - { - nextMarker = fileSystemNames[nextMarkerIndex + 1]; - nextMarker = File.Exists(nextMarker) - ? new FileInfo(nextMarker).Name - : new DirectoryInfo(nextMarker).Name.EnsureEndsWith('/'); - } - // 返回Oss对象描述集合 - var response = new GetOssObjectsResponse( - request.BucketName, - request.Prefix, - request.Marker, - nextMarker, - "/", // 文件系统目录分隔符 - fileSystemNames.Length, - fileSystems.Select(x => new OssObject( - (x is DirectoryInfo) ? x.Name.EnsureEndsWith('/') : x.Name, - request.Prefix, - x.CreationTime, - (x as FileInfo)?.Length ?? 0L, - x.LastWriteTime, - new Dictionary - { - { "LastAccessTime", x.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } - }, - x is DirectoryInfo) - { - FullName = x.FullName.Replace(Environment.ContentRootPath, "") - }) - .ToList()); - - return Task.FromResult(response); - } - - protected virtual FileSystemBlobProviderConfiguration GetFileSystemConfiguration() - { - var configuration = ConfigurationProvider.Get(); - var fileSystemConfiguration = configuration.GetFileSystemConfiguration(); - return fileSystemConfiguration; - } - - protected virtual string CalculateFilePath(string bucketName, string blobName = "") - { - var fileSystemConfiguration = GetFileSystemConfiguration(); - var blobPath = fileSystemConfiguration.BasePath; - - if (CurrentTenant.Id == null) - { - blobPath = Path.Combine(blobPath, "host"); - } - else - { - blobPath = Path.Combine(blobPath, "tenants", CurrentTenant.Id.Value.ToString("D")); - } - - if (fileSystemConfiguration.AppendContainerNameToBasePath && - !bucketName.IsNullOrWhiteSpace()) - { - blobPath = Path.Combine(blobPath, bucketName); - } - if (!blobName.IsNullOrWhiteSpace()) - { - blobPath = Path.Combine(blobPath, blobName); - } - - return blobPath; - } - - private void ThrowOfPathHasTooLong(string path) - { - // Windows 133 260 - // Linux 255 4096 - //if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && path.Length >= 255) // 预留5位 - //{ - // throw new BusinessException(code: OssManagementErrorCodes.OssNameHasTooLong); - //} - } - } -} +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.BlobStoring; +using Volo.Abp.BlobStoring.FileSystem; +using Volo.Abp.IO; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.OssManagement.FileSystem +{ + /// + /// Oss容器的本地文件系统实现 + /// + internal class FileSystemOssContainer : IOssContainer + { + protected ICurrentTenant CurrentTenant { get; } + protected IHostEnvironment Environment { get; } + protected IBlobFilePathCalculator FilePathCalculator { get; } + protected IBlobContainerConfigurationProvider ConfigurationProvider { get; } + protected IServiceProvider ServiceProvider { get; } + protected FileSystemOssOptions Options { get; } + + public FileSystemOssContainer( + ICurrentTenant currentTenant, + IHostEnvironment environment, + IServiceProvider serviceProvider, + IBlobFilePathCalculator blobFilePathCalculator, + IBlobContainerConfigurationProvider configurationProvider, + IOptions options) + { + CurrentTenant = currentTenant; + Environment = environment; + ServiceProvider = serviceProvider; + FilePathCalculator = blobFilePathCalculator; + ConfigurationProvider = configurationProvider; + Options = options.Value; + } + + public virtual Task BulkDeleteObjectsAsync(BulkDeleteObjectRequest request) + { + var objectPath = !request.Path.IsNullOrWhiteSpace() + ? request.Path.EnsureEndsWith('/') + : ""; + var filesPath = request.Objects.Select(x => CalculateFilePath(request.Bucket, objectPath + x)); + + foreach (var file in filesPath) + { + if (Directory.Exists(file)) + { + if (Directory.GetFileSystemEntries(file).Length > 0) + { + throw new BusinessException(code: OssManagementErrorCodes.ContainerDeleteWithNotEmpty); + // throw new ContainerDeleteWithNotEmptyException("00101", $"Can't not delete container {name}, because it is not empty!"); + } + Directory.Delete(file); + } + else if (File.Exists(file)) + { + File.Delete(file); + } + } + + return Task.CompletedTask; + } + + public virtual Task CreateAsync(string name) + { + var filePath = CalculateFilePath(name); + ThrowOfPathHasTooLong(filePath); + if (!Directory.Exists(filePath)) + { + Directory.CreateDirectory(filePath); + } + + var directoryInfo = new DirectoryInfo(filePath); + var container = new OssContainer( + directoryInfo.Name, + directoryInfo.CreationTime, + 0L, + directoryInfo.LastWriteTime, + new Dictionary + { + { "LastAccessTime", directoryInfo.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } + }); + + return Task.FromResult(container); + } + + public virtual async Task CreateObjectAsync(CreateOssObjectRequest request) + { + var objectPath = !request.Path.IsNullOrWhiteSpace() + ? request.Path.EnsureEndsWith('/') + : ""; + var objectName = objectPath.IsNullOrWhiteSpace() + ? request.Object + : objectPath + request.Object; + + var filePath = CalculateFilePath(request.Bucket, objectName); + if (!request.Content.IsNullOrEmpty()) + { + ThrowOfPathHasTooLong(filePath); + + FileMode fileMode = request.Overwrite ? FileMode.Create : FileMode.CreateNew; + if (!request.Overwrite && File.Exists(filePath)) + { + throw new BusinessException(code: OssManagementErrorCodes.ObjectAlreadyExists); + // throw new OssObjectAlreadyExistsException($"Can't not put object {objectName} in container {request.Bucket}, Because a file with the same name already exists in the directory!"); + } + + DirectoryHelper.CreateIfNotExists(Path.GetDirectoryName(filePath)); + + using (var fileStream = File.Open(filePath, fileMode, FileAccess.Write)) + { + await request.Content.CopyToAsync(fileStream); + + await fileStream.FlushAsync(); + } + var fileInfo = new FileInfo(filePath); + var ossObject = new OssObject( + fileInfo.Name, + objectPath, + fileInfo.CreationTime, + fileInfo.Length, + fileInfo.LastWriteTime, + new Dictionary + { + { "IsReadOnly", fileInfo.IsReadOnly.ToString() }, + { "LastAccessTime", fileInfo.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } + }) + { + FullName = fileInfo.FullName.Replace(Environment.ContentRootPath, "") + }; + ossObject.SetContent(request.Content); + + return ossObject; + } + else + { + ThrowOfPathHasTooLong(filePath); + if (Directory.Exists(filePath)) + { + throw new BusinessException(code: OssManagementErrorCodes.ObjectAlreadyExists); + // throw new OssObjectAlreadyExistsException($"Can't not put object {objectName} in container {request.Bucket}, Because a file with the same name already exists in the directory!"); + } + Directory.CreateDirectory(filePath); + var directoryInfo = new DirectoryInfo(filePath); + + var ossObject = new OssObject( + directoryInfo.Name.EnsureEndsWith('/'), + objectPath, + directoryInfo.CreationTime, + 0L, + directoryInfo.LastWriteTime, + new Dictionary + { + { "LastAccessTime", directoryInfo.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } + }, + true) + { + FullName = directoryInfo.FullName.Replace(Environment.ContentRootPath, "") + }; + ossObject.SetContent(request.Content); + + return ossObject; + } + } + + public virtual Task DeleteAsync(string name) + { + var filePath = CalculateFilePath(name); + if (!Directory.Exists(filePath)) + { + throw new BusinessException(code: OssManagementErrorCodes.ContainerNotFound); + } + // 非空目录无法删除 + if (Directory.GetFileSystemEntries(filePath).Length > 0) + { + throw new BusinessException(code: OssManagementErrorCodes.ContainerDeleteWithNotEmpty); + // throw new ContainerDeleteWithNotEmptyException("00101", $"Can't not delete container {name}, because it is not empty!"); + } + Directory.Delete(filePath); + + return Task.CompletedTask; + } + + public virtual Task DeleteObjectAsync(GetOssObjectRequest request) + { + var objectName = request.Path.IsNullOrWhiteSpace() + ? request.Object + : request.Path.EnsureEndsWith('/') + request.Object; + var filePath = CalculateFilePath(request.Bucket, objectName); + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + else if (Directory.Exists(filePath)) + { + if (Directory.GetFileSystemEntries(filePath).Length > 0) + { + throw new BusinessException(code: OssManagementErrorCodes.ObjectDeleteWithNotEmpty); + } + Directory.Delete(filePath); + } + + return Task.CompletedTask; + } + + public virtual Task ExistsAsync(string name) + { + var filePath = CalculateFilePath(name); + + return Task.FromResult(Directory.Exists(filePath)); + } + + public virtual Task GetAsync(string name) + { + var filePath = CalculateFilePath(name); + if (!Directory.Exists(filePath)) + { + throw new BusinessException(code: OssManagementErrorCodes.ContainerNotFound); + // throw new ContainerNotFoundException($"Can't not found container {name} in file system"); + } + + var directoryInfo = new DirectoryInfo(filePath); + var container = new OssContainer( + directoryInfo.Name, + directoryInfo.CreationTime, + 0L, + directoryInfo.LastWriteTime, + new Dictionary + { + { "LastAccessTime", directoryInfo.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } + }); + + return Task.FromResult(container); + } + + public virtual async Task GetObjectAsync(GetOssObjectRequest request) + { + var objectPath = !request.Path.IsNullOrWhiteSpace() + ? request.Path.EnsureEndsWith('/') + : ""; + var objectName = objectPath.IsNullOrWhiteSpace() + ? request.Object + : objectPath + request.Object; + + var filePath = CalculateFilePath(request.Bucket, objectName); + if (!File.Exists(filePath)) + { + if (!Directory.Exists(filePath)) + { + throw new BusinessException(code: OssManagementErrorCodes.ObjectNotFound); + // throw new ContainerNotFoundException($"Can't not found object {objectName} in container {request.Bucket} with file system"); + } + var directoryInfo = new DirectoryInfo(filePath); + var ossObject = new OssObject( + directoryInfo.Name.EnsureEndsWith('/'), + objectPath, + directoryInfo.CreationTime, + 0L, + directoryInfo.LastWriteTime, + new Dictionary + { + { "LastAccessTime", directoryInfo.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } + }, + true) + { + FullName = directoryInfo.FullName.Replace(Environment.ContentRootPath, "") + }; + return ossObject; + } + else + { + var fileInfo = new FileInfo(filePath); + var ossObject = new OssObject( + fileInfo.Name, + objectPath, + fileInfo.CreationTime, + fileInfo.Length, + fileInfo.LastWriteTime, + new Dictionary + { + { "IsReadOnly", fileInfo.IsReadOnly.ToString() }, + { "LastAccessTime", fileInfo.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } + }) + { + FullName = fileInfo.FullName.Replace(Environment.ContentRootPath, "") + }; + using (var fileStream = File.OpenRead(filePath)) + { + var memoryStream = new MemoryStream(); + await fileStream.CopyToAsync(memoryStream); + ossObject.SetContent(memoryStream); + + if (!request.Process.IsNullOrWhiteSpace()) + { + using var serviceScope = ServiceProvider.CreateScope(); + var context = new FileSystemOssObjectContext(request.Process, ossObject, serviceScope.ServiceProvider); + foreach (var processer in Options.Processers) + { + await processer.ProcessAsync(context); + + if (context.Handled) + { + ossObject.SetContent(context.Content); + break; + } + } + } + } + + return ossObject; + } + } + + public virtual Task GetListAsync(GetOssContainersRequest request) + { + // 不传递Bucket 检索根目录的Bucket + var filePath = CalculateFilePath(null); + + // 获取根目录 + var directories = Directory.GetDirectories(filePath, request.Prefix ?? "*.*"); + + // 排序目录 + Array.Sort(directories, delegate (string x, string y) + { + return x.CompareTo(y); + }); + + var spiltDirectories = directories; + // 计算标记的位置进行截断 + if (!request.Marker.IsNullOrWhiteSpace()) + { + var markIndex = directories.FindIndex(x => x.EndsWith(request.Marker)); + if (markIndex < 0) + { + directories = new string[0]; + } + else + { + var markDirectories = new string[directories.Length - markIndex]; + Array.Copy(directories, markIndex, markDirectories, 0, markDirectories.Length); + directories = markDirectories; + } + } + // 需要截断最大的容器集合 + if (request.MaxKeys.HasValue) + { + spiltDirectories = directories.Take(request.MaxKeys ?? directories.Length).ToArray(); + } + var nextDirectory = spiltDirectories.Length < directories.Length ? directories[spiltDirectories.Length] : ""; + if (!nextDirectory.IsNullOrWhiteSpace()) + { + // 下一个标记的目录名称 + + nextDirectory = new DirectoryInfo(nextDirectory).Name; + } + // 容器对应的目录信息集合 + var directoryInfos = spiltDirectories.Select(x => new DirectoryInfo(x)); + // 返回Oss容器描述集合 + var response = new GetOssContainersResponse( + request.Prefix, + request.Marker, + nextDirectory, + directories.Length, + directoryInfos.Select(x => new OssContainer( + x.Name, + x.CreationTime, + 0L, + x.LastWriteTime, + new Dictionary + { + { "LastAccessTime", x.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } + })) + .ToList()); + + return Task.FromResult(response); + } + + public virtual Task GetObjectsAsync(GetOssObjectsRequest request) + { + // 先定位检索的目录 + var filePath = CalculateFilePath(request.BucketName, request.Prefix); + if (!Directory.Exists(filePath)) + { + throw new BusinessException(code: OssManagementErrorCodes.ContainerNotFound); + // throw new ContainerNotFoundException($"Can't not found container {request.BucketName} in file system"); + } + // 目录也属于Oss对象,需要抽象的文件系统集合来存储 + var fileSystemNames = Directory.GetFileSystemEntries(filePath); + int maxFilesCount = fileSystemNames.Length; + + // 排序所有文件与目录 + Array.Sort(fileSystemNames, delegate (string x, string y) + { + // 检索的是文件系统名称 + // 需要判断是否为文件夹进行升序排序 + // 参考 OssObjectComparer + + var xFolder = Directory.Exists(x); + var yFolder = Directory.Exists(y); + + if (xFolder && yFolder) + { + return x.CompareTo(y); + } + + if (xFolder && !yFolder) + { + return -1; + } + + if (!xFolder && yFolder) + { + return 1; + } + + return x.CompareTo(y); + }); + + // 需要计算从哪个位置截断 + int markIndex = 0; + if (!request.Marker.IsNullOrWhiteSpace()) + { + markIndex = fileSystemNames.FindIndex(x => x.EndsWith(request.Marker)); + if (markIndex < 0) + { + markIndex = 0; + } + } + + // 需要截断Oss对象列表 + var copyFileSystemNames = fileSystemNames; + if (markIndex > 0) + { + // fix: 翻页查询数组可能引起下标越界 + // copyFileSystemNames = fileSystemNames[(markIndex+1)..]; + copyFileSystemNames = fileSystemNames[markIndex..]; + } + // 截取指定数量的Oss对象 + int maxResultCount = request.MaxKeys ?? 10; + // Oss对象信息集合 + var fileSystems = copyFileSystemNames + .Take(maxResultCount) + .Select(file => + { + if (File.Exists(file)) + { + return new FileInfo(file); + } + return new DirectoryInfo(file); + }) + .ToArray(); + + // 计算下一页起始标记文件/目录名称 + var nextMarkerIndex = fileSystemNames.FindIndex(x => x.EndsWith(fileSystems[fileSystems.Length - 1].Name)); + string nextMarker = ""; + if (nextMarkerIndex >=0 && nextMarkerIndex + 1 < fileSystemNames.Length) + { + nextMarker = fileSystemNames[nextMarkerIndex + 1]; + nextMarker = File.Exists(nextMarker) + ? new FileInfo(nextMarker).Name + : new DirectoryInfo(nextMarker).Name.EnsureEndsWith('/'); + } + // 返回Oss对象描述集合 + var response = new GetOssObjectsResponse( + request.BucketName, + request.Prefix, + request.Marker, + nextMarker, + "/", // 文件系统目录分隔符 + fileSystemNames.Length, + fileSystems.Select(x => new OssObject( + (x is DirectoryInfo) ? x.Name.EnsureEndsWith('/') : x.Name, + request.Prefix, + x.CreationTime, + (x as FileInfo)?.Length ?? 0L, + x.LastWriteTime, + new Dictionary + { + { "LastAccessTime", x.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss") } + }, + x is DirectoryInfo) + { + FullName = x.FullName.Replace(Environment.ContentRootPath, "") + }) + .ToList()); + + return Task.FromResult(response); + } + + protected virtual FileSystemBlobProviderConfiguration GetFileSystemConfiguration() + { + var configuration = ConfigurationProvider.Get(); + var fileSystemConfiguration = configuration.GetFileSystemConfiguration(); + return fileSystemConfiguration; + } + + protected virtual string CalculateFilePath(string bucketName, string blobName = "") + { + var fileSystemConfiguration = GetFileSystemConfiguration(); + var blobPath = fileSystemConfiguration.BasePath; + + if (CurrentTenant.Id == null) + { + blobPath = Path.Combine(blobPath, "host"); + } + else + { + blobPath = Path.Combine(blobPath, "tenants", CurrentTenant.Id.Value.ToString("D")); + } + + if (fileSystemConfiguration.AppendContainerNameToBasePath && + !bucketName.IsNullOrWhiteSpace()) + { + blobPath = Path.Combine(blobPath, bucketName); + } + if (!blobName.IsNullOrWhiteSpace()) + { + blobPath = Path.Combine(blobPath, blobName); + } + + return blobPath; + } + + private void ThrowOfPathHasTooLong(string path) + { + // Windows 133 260 + // Linux 255 4096 + //if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && path.Length >= 255) // 预留5位 + //{ + // throw new BusinessException(code: OssManagementErrorCodes.OssNameHasTooLong); + //} + } + } +} From 0b2a16ab16566f9fd228638165f4da1c10d8b838 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Mon, 11 Oct 2021 10:14:12 +0800 Subject: [PATCH 2/2] fix bug(Oss-FileSystem): retrieves the disk root directory by / --- .../Abp/OssManagement/FileSystem/FileSystemOssContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.FileSystem/LINGYUN/Abp/OssManagement/FileSystem/FileSystemOssContainer.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.FileSystem/LINGYUN/Abp/OssManagement/FileSystem/FileSystemOssContainer.cs index 1edab3676..0d29fd6c4 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.FileSystem/LINGYUN/Abp/OssManagement/FileSystem/FileSystemOssContainer.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.FileSystem/LINGYUN/Abp/OssManagement/FileSystem/FileSystemOssContainer.cs @@ -523,6 +523,8 @@ namespace LINGYUN.Abp.OssManagement.FileSystem } if (!blobName.IsNullOrWhiteSpace()) { + // fix: If the user passes /, the disk root directory is retrieved + blobName = blobName.Equals("/") ? "./" : blobName; blobPath = Path.Combine(blobPath, blobName); }