using System; using System.IO; using System.IO.Abstractions; using System.Security.Principal; using System.Text; using System.Threading.Tasks; using BO.AppServer.Business.Exceptions; using BO.AppServer.Data.Entity; using BO.AppServer.Data.Repository; using BO.AppServer.Metadata.Enums; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Quadarax.Foundation.Core.Business; using Quadarax.Foundation.Core.Exceptions; using Quadarax.Foundation.Core.Value.Extensions; namespace BO.AppServer.Business.Services { public class StorageService : AbstractService { #region *** Private fields *** private IFileSystem _fileSystem; private IOptions _config; private ArtifactRepo _repoArtifact; #endregion #region *** Constructors *** public StorageService(ArtifactRepo repoArtifact, IOptions config, IFileSystem fileSystem, IPrincipal currentPrincipal, ILoggerFactory logger) : base(currentPrincipal, logger) { _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); _config = config ?? throw new ArgumentNullException(nameof(config)); _repoArtifact = repoArtifact ?? throw new ArgumentNullException(nameof(repoArtifact)); if (!_fileSystem.Directory.Exists(_config.Value.System.Storage.InputPath.EnsurePathNonBackslash())) { _fileSystem.Directory.CreateDirectory(_config.Value.System.Storage.InputPath.EnsurePathNonBackslash()); Log.LogInformation($"Storage Input directory '{_config.Value.System.Storage.InputPath.EnsurePathNonBackslash()}' created."); } else Log.LogInformation($"Storage Input directory '{_config.Value.System.Storage.InputPath.EnsurePathNonBackslash()}' verified."); if (!_fileSystem.Directory.Exists(_config.Value.System.Storage.OutputPath.EnsurePathNonBackslash())) { _fileSystem.Directory.CreateDirectory(_config.Value.System.Storage.OutputPath.EnsurePathNonBackslash()); Log.LogInformation($"Storage Output directory '{_config.Value.System.Storage.OutputPath.EnsurePathNonBackslash()}' created."); } else Log.LogInformation($"Storage Input directory '{_config.Value.System.Storage.InputPath.EnsurePathNonBackslash()}' verified."); } #endregion #region *** Public Operations *** public async Task PushContentAsync(long artifactId, Stream fileData) { var artifact = _repoArtifact.Get(artifactId); if (artifact == null) { var exc = new EntityNotExistsException(typeof(Artifact),nameof(Artifact.Id), artifactId); Log.LogWarning(exc.Message); throw exc; } var documentId = artifact.Metadocument.Id; var workspaceId = artifact.Metadocument.Structure.WorkspaceId; var fileName = GetFileName(artifactId, artifact.Name, artifact.Extension); var artType = (ArtifactTypeEnum)artifact.Type; var fullFileName = EnureDirectory(workspaceId, documentId, artType) + _fileSystem.Path.DirectorySeparatorChar + fileName; long size; await using (var fs = _fileSystem.File.Create(fullFileName, _config.Value.System.Storage.IoBufferSize, FileOptions.Asynchronous)) { fileData.Position = 0; await fileData.CopyToAsync(fs, _config.Value.System.Storage.IoBufferSize); await fs.FlushAsync(); size = fs.Length; fs.Close(); } return size; } public async Task> PopContentAsync(long artifactId) { var artifact = _repoArtifact.Get(artifactId); if (artifact == null) { var exc = new EntityNotExistsException(typeof(Artifact),nameof(Artifact.Id), artifactId); Log.LogWarning(exc.Message); throw exc; } var documentId = artifact.Metadocument.Id; var workspaceId = artifact.Metadocument.Structure.WorkspaceId; var fileName = GetFileName(artifactId, artifact.Name, artifact.Extension); var artType = (ArtifactTypeEnum)artifact.Type; var fullFileName = EnureDirectory(workspaceId, documentId, artType) + _fileSystem.Path.DirectorySeparatorChar + fileName; if (!_fileSystem.File.Exists(fullFileName)) { var message = $"Content for artifact '{fullFileName}' [{artifactId}] not found!"; Log.LogWarning(message); throw new CodeException(1300, message); } var buffer = new byte[_config.Value.System.Storage.IoBufferSize]; //var outputStream = new MemoryStream(buffer); var outputStream = new MemoryStream(); await using (var fs = _fileSystem.File.OpenRead(fullFileName)) { //await fs.CopyToAsync(outputStream, _config.Value.System.Storage.IoBufferSize); await fs.CopyToAsync(outputStream); await outputStream.FlushAsync(); fs.Close(); } outputStream.Seek(0, SeekOrigin.Begin); return new Tuple(outputStream, outputStream.Length); } public async Task DeleteContent(long artifactId) { var artifact = _repoArtifact.Get(artifactId); if (artifact == null) { var exc = new EntityNotExistsException(typeof(Artifact),nameof(Artifact.Id), artifactId); Log.LogWarning(exc.Message); throw exc; } var documentId = artifact.Metadocument.Id; var workspaceId = artifact.Metadocument.Structure.WorkspaceId; var fileName = GetFileName(artifactId, artifact.Name, artifact.Extension); var artType = (ArtifactTypeEnum)artifact.Type; var fullFileName = EnureDirectory(workspaceId, documentId, artType) + _fileSystem.Path.DirectorySeparatorChar + fileName; if (!_fileSystem.File.Exists(fullFileName)) { var message = $"Content for artifact '{fullFileName}' [{artifactId}] not found for delete!"; Log.LogInformation(message); return false; } _fileSystem.File.Delete(fullFileName); return true; } #endregion #region *** Private Operations *** private string EnureDirectory(long workspaceId, long documentId, ArtifactTypeEnum type) { var dirName = GetDirectoryName(workspaceId, documentId, type); if (!_fileSystem.Directory.Exists(dirName)) { _fileSystem.Directory.CreateDirectory(dirName); Log.LogInformation($"Storage document directory '{dirName}' created."); } return dirName; } private string GetDirectoryName(long workspaceId, long documentId, ArtifactTypeEnum type) { var sb = new StringBuilder(); sb.Append(type == ArtifactTypeEnum.Input ? _config.Value.System.Storage.InputPath.EnsurePathNonBackslash() : _config.Value.System.Storage.OutputPath.EnsurePathNonBackslash()).Append(_fileSystem.Path.DirectorySeparatorChar); sb.Append(workspaceId).Append(_fileSystem.Path.DirectorySeparatorChar); sb.Append(documentId); return sb.ToString(); } private string GetFileName(long artifactId, string fileNameWoExtension, string extension) { var sb = new StringBuilder(); sb.Append(fileNameWoExtension).Append("."); sb.Append(artifactId).Append("."); sb.Append(extension); return sb.ToString(); } #endregion } }