public async Task PublishReleaseFilesAsync(Guid releaseId)
        {
            var release = await _releaseService.GetAsync(releaseId);

            var copyReleaseCommand = new CopyReleaseFilesCommand
            {
                ReleaseId        = releaseId,
                PublicationSlug  = release.Publication.Slug,
                PublishScheduled = release.PublishScheduled.Value,
                ReleaseSlug      = release.Slug,
                Files            = await _releaseService.GetFiles(releaseId,
                                                                  Ancillary,
                                                                  Chart,
                                                                  FileType.Data)
            };
            await _fileStorageService.CopyReleaseFilesToPublicContainer(copyReleaseCommand);
        }
        public async Task CopyReleaseFilesToPublicContainer(CopyReleaseFilesCommand copyReleaseFilesCommand)
        {
            var destinationDirectoryPath =
                PublicReleaseDirectoryPath(copyReleaseFilesCommand.PublicationSlug, copyReleaseFilesCommand.ReleaseSlug);

            await _publicBlobStorageService.DeleteBlobs(PublicFilesContainerName, destinationDirectoryPath);

            var referencedReleaseVersions = copyReleaseFilesCommand.Files
                                            .Select(f => f.ReleaseId)
                                            .Distinct();

            var transferredFiles = new List <BlobInfo>();

            foreach (var version in referencedReleaseVersions)
            {
                var files = await CopyPrivateFilesToPublic(
                    sourceDirectoryPath : AdminReleaseDirectoryPath(version),
                    destinationDirectoryPath : destinationDirectoryPath,
                    copyReleaseFilesCommand
                    );

                transferredFiles.AddRange(files);
            }

            var chartDirectory = PublicReleaseDirectoryPath(
                copyReleaseFilesCommand.PublicationSlug,
                copyReleaseFilesCommand.ReleaseSlug,
                Chart
                );

            var zipFiles = transferredFiles
                           .Where(blob => !blob.Path.StartsWith(chartDirectory))
                           .ToList();

            await UploadZippedFiles(
                PublicFilesContainerName,
                destinationPath : PublicReleaseAllFilesZipPath(
                    copyReleaseFilesCommand.PublicationSlug,
                    copyReleaseFilesCommand.ReleaseSlug),
                zipFileName : "All files",
                files : zipFiles,
                copyReleaseFilesCommand : copyReleaseFilesCommand
                );
        }
        private async Task UploadZippedFiles(
            string containerName,
            string destinationPath,
            string zipFileName,
            IEnumerable <BlobInfo> files,
            CopyReleaseFilesCommand copyReleaseFilesCommand)
        {
            await using var memoryStream    = new MemoryStream();
            await using var zipOutputStream = new ZipOutputStream(memoryStream);

            zipOutputStream.SetLevel(1);

            foreach (var file in files)
            {
                var zipEntry = new ZipEntry(file.FileName);
                zipOutputStream.PutNextEntry(zipEntry);

                await _publicBlobStorageService.DownloadToStream(containerName, file.Path, zipOutputStream);
            }

            zipOutputStream.Finish();

            await _publicBlobStorageService.UploadStream(
                containerName,
                destinationPath,
                memoryStream,
                // Should this be MetaTypeNames.Application.Zip?
                contentType : "application/x-zip-compressed",
                options : new IBlobStorageService.UploadStreamOptions
            {
                MetaValues = GetAllFilesZipMetaValues(
                    name: zipFileName,
                    releaseDateTime: copyReleaseFilesCommand.PublishScheduled)
            }
                );
        }
 private async Task <List <BlobInfo> > CopyPrivateFilesToPublic(
     string sourceDirectoryPath,
     string destinationDirectoryPath,
     CopyReleaseFilesCommand copyReleaseFilesCommand)
 {
     return(await _privateBlobStorageService.CopyDirectory(
                sourceContainerName : PrivateFilesContainerName,
                sourceDirectoryPath : sourceDirectoryPath,
                destinationContainerName : PublicFilesContainerName,
                destinationDirectoryPath : destinationDirectoryPath,
                new IBlobStorageService.CopyDirectoryOptions
     {
         DestinationConnectionString = _publicStorageConnectionString,
         SetAttributesCallbackAsync = (destination) =>
                                      SetAttributesCallbackAsync(destination, copyReleaseFilesCommand.PublishScheduled),
         ShouldTransferCallbackAsync = (source, _) =>
                                       CopyFileUnlessBatchedOrMeta(
             source: source,
             sourceContainerName: PrivateFilesContainerName,
             releaseId: copyReleaseFilesCommand.ReleaseId,
             files: copyReleaseFilesCommand.Files)
     }
                ));
 }