I need to ZIP and GPG Encrypt a Stream of files.
Then upload via SFTP.
I am using Zip Archive to create entries.
I am using GPG to encrypt, starksoft.aspen Nuget.
Getting local file streams here and returning an I Enumerable of local file streams
private static IEnumerable<LocalFile> GetLocalFiles(string dir) =>
Directory.EnumerateFiles(dir, "*", SearchOption.AllDirectories)
.Select(path =>
{
var relativePath = path.Substring(dir.Length + 1);
var localFile = new LocalFile(relativePath, () => File.OpenRead(path));
localFile.Folder = Directory.GetDirectories(dir, "*", SearchOption.TopDirectoryOnly)
.Select(d => d.Split('\\').LastOrDefault()).FirstOrDefault();
return localFile;
});
Then I zip the IEnumerable of local streams
public async Task WriteAsync(IEnumerable<LocalFile> files)
{
FileStream GetTempFileStream() =>
new FileStream(
path: Path.GetTempFileName(),
mode: FileMode.Open,
access: FileAccess.ReadWrite,
share: FileShare.None,
bufferSize: 4096,
options: FileOptions.DeleteOnClose);
//temp fix to resolve the arb naming for zip files
var folder = files.Select(x => x.Folder).FirstOrDefault()
?? DateTime.Now.ToString("yyyy-MM-dd hh-mm-ss");
var zipFile = new LocalFile(
folder+".zip",
async () =>
{
var tempFileStream = GetTempFileStream();
using (var archive = new ZipArchive(tempFileStream, ZipArchiveMode.Create, true))
{
foreach (var file in files)
{
using (var localStream = (await file.OpenLocal()))
using (var zipEntryStream = archive.CreateEntry(file.RelativePath).Open())
{
await localStream.CopyToAsync(zipEntryStream);
}
}
}
tempFileStream.Seek(0, SeekOrigin.Begin);
return tempFileStream;
});
Then I need to encrypt the IEnumerable of zip stream...
Thank you
got to the bottom of this
The problem I had was the stream was not done encrypting before being passed by to my Writer.
Here is what I did to zip and encrypt , I hope this helps someone
public async Task WriteAsync(IEnumerable<LocalFile> files)
{
var folder = files.Select(x => x.Folder).FirstOrDefault()
?? DateTime.Now.ToString("yyyy-MM-dd hh-mm-ss");
using (var archiveStream = new MemoryStream())
{
using (var archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, true))
{
foreach (var file in files)
{
using (var entry = archive.CreateEntry(file.RelativePath).Open())
using (var local = (await file.OpenLocal()))
{
local.CopyTo(entry);
}
}
}
var bytes = archiveStream.ToArray();
var encryptionClient = new EncryptionClient("ahmad.zeitoun#medxm1.com", "Password7");
var encryption = new EncryptFiles(encryptionClient);
using (var encryptedStream = new MemoryStream())
{
using (var zipStream = new MemoryStream(bytes))
{
encryption.Gpg.Encrypt(zipStream, encryptedStream);
}
this.bytesArr = encryptedStream.ToArray();
}
}
await this.Writer.WriteAsync(new[] { new LocalFile(folder + ".zip.gpg", () => new MemoryStream(this.bytesArr)) });
Related
Here is my method I am building to add files in Azure Blob Storage to a zip archive.
I am not sure how to get the blob files to the archive.
public async Task<System.IO.File> CreateLoanFilesZip(string loanFileId)
{
var appList = _unitOfWork.Applications.GetByLoanFile(loanFileId);
if (appList == null)
{
return null;
}
string connection = _appConfig.AzureStorageConnection;
string containerName = _appConfig.AzureStorageContainer;
//create azure service/container client
var serviceClient = new BlobServiceClient(connection);
var container = serviceClient.GetBlobContainerClient(containerName);
BlobClient blob;
//loop each row in list of loan file applications
foreach (var app in appList)
{
//get list of files from documents repo by application
var fileList = _unitOfWork.Documents.GetListByApplication(app.ApplicationId);
if (fileList != null)
{
//create a zip file with loan number and application id?
using (var memoryStream = new MemoryStream())
{
using (var zip = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach(var file in fileList)
{
blob = container.GetBlobClient(file.UniqueDocumentName);
??--> do I need to stream the blob into the zip??
zip.CreateEntryFromFile(file.UniqueDocumentName, blob);
}
}
memoryStream.Position = 0;
return File(memoryStream, "application/zip", "application-file-download.zip");
}
}
}
}
I am not sure how to take the blob object and get it into the CreateEntryFromFile method. Do I need to create a separate stream of byte[] content?
Update
This error occurs because the memory stream used in the code is released after executing the return statement. The File method creates a FileStreamResult wrapping the memory stream and returns it, but since the memory stream has been deallocated, the FileStreamResult cannot access it.
You can use FileStreamResult to fix it, not System.IO.File.
public async Task<FileStreamResult> CreateLoanFilesZip(string loanFileId)
{
var appList = _unitOfWork.Applications.GetByLoanFile(loanFileId);
if (appList == null)
{
return null;
}
string connection = _appConfig.AzureStorageConnection;
string containerName = _appConfig.AzureStorageContainer;
//create azure service/container client
var serviceClient = new BlobServiceClient(connection);
var container = serviceClient.GetBlobContainerClient(containerName);
BlobClient blob;
//loop each row in list of loan file applications
foreach (var app in appList)
{
//get list of files from documents repo by application
var fileList = _unitOfWork.Documents.GetListByApplication(app.ApplicationId);
if (fileList != null)
{
//create a zip file with loan number and application id?
var memoryStream = new MemoryStream();
var fileStreamResult = new FileStreamResult(memoryStream, "application/zip")
{
FileDownloadName = "application-file-download.zip"
};
using (var zip = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach(var file in fileList)
{
blob = container.GetBlobClient(file.UniqueDocumentName);
var blobResponse = await blob.DownloadAsync();
using (var streamReader = new StreamReader(blobResponse.Value.Content))
{
var entry = zip.CreateEntry(file.UniqueDocumentName);
using (var entryStream = entry.Open())
{
await streamReader.BaseStream.CopyToAsync(entryStream);
}
}
}
}
memoryStream.Position = 0;
return fileStreamResult;
}
}
}
You need to download the contents of the blob as a stream of bytes and then add those bytes to the archive. Here is the sample.
public async Task<System.IO.File> CreateLoanFilesZip(string loanFileId)
{
var appList = _unitOfWork.Applications.GetByLoanFile(loanFileId);
if (appList == null)
{
return null;
}
string connection = _appConfig.AzureStorageConnection;
string containerName = _appConfig.AzureStorageContainer;
//create azure service/container client
var serviceClient = new BlobServiceClient(connection);
var container = serviceClient.GetBlobContainerClient(containerName);
BlobClient blob;
//loop each row in list of loan file applications
foreach (var app in appList)
{
//get list of files from documents repo by application
var fileList = _unitOfWork.Documents.GetListByApplication(app.ApplicationId);
if (fileList != null)
{
//create a zip file with loan number and application id?
using (var memoryStream = new MemoryStream())
{
using (var zip = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach(var file in fileList)
{
blob = container.GetBlobClient(file.UniqueDocumentName);
var blobResponse = await blob.DownloadAsync();
using (var streamReader = new StreamReader(blobResponse.Value.Content))
{
var entry = zip.CreateEntry(file.UniqueDocumentName);
using (var entryStream = entry.Open())
{
await streamReader.BaseStream.CopyToAsync(entryStream);
}
}
}
}
memoryStream.Position = 0;
return File(memoryStream, "application/zip", "application-file-download.zip");
}
}
}
}
I want to create a .zip file in C# and after that I want to put a xml file in it.
This is what I tried but it doesn't work.
I have a folder like, archieve -> 2040 -> and here I want to create the archieve
private void WriteXmlArchieveProductOnDisk(string identifier, string pathInFolder, byte[] xmlFile)
{
string folderName = Path.Combine("Resources", pathInFolder, _baseService.CurrentCompanyId().ToString());
string pathToSave = Path.Combine(Directory.GetCurrentDirectory(), folderName);
if (!Directory.Exists(pathToSave))
{
Directory.CreateDirectory(pathToSave);
}
string fileNameWithoutExtension = $"{Guid.NewGuid()}_{identifier}";
string fileNameWithExtension = $"{fileNameWithoutExtension}.xml";
List<string> existing = Directory.EnumerateFiles(pathToSave).Where(file => Path.GetFileNameWithoutExtension(file) == fileNameWithoutExtension).ToList();
if (existing.Count > 0)
{
foreach (var file in existing)
{
FileD.Delete(file);
}
}
using (var ms = new MemoryStream())
{
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
var zipArchiveEntry = archive.CreateEntry(fileNameWithExtension);
using (var entryStream = zipArchiveEntry.Open())
using (var fileToCompressStream = new StreamWriter(entryStream))
{
fileToCompressStream.Write(xmlFile);
}
}
using (var fileStream = new FileStream($"{pathToSave}", FileMode.Create))
{
ms.Seek(0, SeekOrigin.Begin);
ms.CopyTo(fileStream);
}
}
}
What I'm looking for is zip/compress S3 files without having them first downloaded to EFS or on a file system and then upload the zip file back to S3. Is there a C# way to achieve the same? I found the following post, but not sure its C# equivalent
https://www.antstack.io/blog/create-zip-using-lambda-with-files-streamed-from-s3/
I've written following code to zip files from a MemoryStream
public static void CreateZip(string zipFileName, List<FileInfo> filesToZip)
{
//zipFileName is the final zip file name
LambdaLogger.Log($"Zipping in progress for: {zipFileName}");
using (MemoryStream zipMS = new MemoryStream())
{
using (ZipArchive zipArchive = new ZipArchive(zipMS, ZipArchiveMode.Create, true))
{
//loop through files to add
foreach (var fileToZip in filesToZip)
{
//read the file bytes
byte[] fileToZipBytes = File.ReadAllBytes(fileToZip.FullName);
ZipArchiveEntry zipFileEntry = zipArchive.CreateEntry(fileToZip.Name);
//add the file contents
using (Stream zipEntryStream = zipFileEntry.Open())
using (BinaryWriter zipFileBinary = new BinaryWriter(zipEntryStream))
{
zipFileBinary.Write(fileToZipBytes);
}
}
}
using (FileStream finalZipFileStream = new FileStream(zipFileName, FileMode.Create))
{
zipMS.Seek(0, SeekOrigin.Begin);
zipMS.CopyTo(finalZipFileStream);
}
}
}
But problem is how to make it read file directly from S3 and upload the compressed file.
public static async Task CreateZipFile(List<List<KeyVersion>> keyVersions)
{
using MemoryStream zipMS = new MemoryStream();
using (ZipArchive zipArchive = new ZipArchive(zipMS, ZipArchiveMode.Create, true))
{
foreach (var key in keyVersions)
{
foreach (var fileToZip in key)
{
GetObjectRequest request = new GetObjectRequest
{
BucketName = "dev-s3-zip-bucket",
Key = fileToZip.Key
};
using GetObjectResponse response = await s3client.GetObjectAsync(request);
using Stream responseStream = response.ResponseStream;
ZipArchiveEntry zipFileEntry = zipArchive.CreateEntry(fileToZip.Key);
//add the file contents
using Stream zipEntryStream = zipFileEntry.Open();
await responseStream.CopyToAsync(zipEntryStream);
}
}
zipArchive.Dispose();
}
zipMS.Seek(0, SeekOrigin.Begin);
var fileTxfr = new TransferUtility(s3client);
await fileTxfr.UploadAsync(zipMS, "dev-s3-zip-bucket", "test.zip");
}
I tried below code to first convert my bytes to compressed bytes and try to create a zip file, but file is not generated. Could someone please suggest!
byte[] result;
var X = 86251;
byte[] compressedBytes;
using (FileStream SourceStream = File.Open(filename, FileMode.Open))
{
SourceStream.Seek(0, SeekOrigin.Begin);
result = new byte[X];
await SourceStream.ReadAsync(result, 0, X);
}
string fileName = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";
using (var outStream = new MemoryStream())
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry(fileName, CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = new MemoryStream(result))
{
fileToCompressStream.CopyTo(entryStream);
}
}
compressedBytes = outStream.ToArray();
}
You're not creating an actual file anywhere. You're just writing to a memory stream. Change it to write to a file.
using (var outStream = new File.Create(fileName))
{
using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
{
var fileInArchive = archive.CreateEntry(filename, CompressionLevel.Optimal);
using (var entryStream = fileInArchive.Open())
using (var fileToCompressStream = new MemoryStream(result))
{
fileToCompressStream.CopyTo(entryStream);
}
}
}
I am building a metro style app for windows 8 and I have a zip file that I am downloading from a web service, and I want to extract it.
I have seen the sample for compression and decompression, but that takes a single file an compresses/decompresses it. I have a whole directory structure that I need to extract.
Here is what I have so far:
var appData = ApplicationData.Current;
var file = await appData.LocalFolder.GetItemAsync("thezip.zip") as StorageFile;
var decompressedFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("tempFileName", CreationCollisionOption.GenerateUniqueName);
using (var decompressor = new Decompressor(await file.OpenSequentialReadAsync()))
using (var decompressedOutput = await decompressedFile.OpenAsync(FileAccessMode.ReadWrite))
{
var bytesDecompressed = await RandomAccessStream.CopyAsync(decompressor, decompressedOutput);
}
But this is no good, the bytesDecompressed variable is always zero size, but the zip File is 1.2MB
Any help here would be greatly appreciated.
EDIT: Answer, thanks to Mahantesh
Here is the code for unzipping a file:
private async void UnZipFile()
{
var folder = ApplicationData.Current.LocalFolder;
using (var zipStream = await folder.OpenStreamForReadAsync("thezip.zip"))
{
using (MemoryStream zipMemoryStream = new MemoryStream((int)zipStream.Length))
{
await zipStream.CopyToAsync(zipMemoryStream);
using (var archive = new ZipArchive(zipMemoryStream, ZipArchiveMode.Read))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.Name != "")
{
using (Stream fileData = entry.Open())
{
StorageFile outputFile = await folder.CreateFileAsync(entry.Name, CreationCollisionOption.ReplaceExisting);
using (Stream outputFileStream = await outputFile.OpenStreamForWriteAsync())
{
await fileData.CopyToAsync(outputFileStream);
await outputFileStream.FlushAsync();
}
}
}
}
}
}
}
}
In Metro style apps, you work with compressed files by using the methods in the ZipArchive, ZipArchiveEntry, DeflateStream, and GZipStream classes.
Refer : UnZip File in Metro
Refer : Folder zip/unzip in metro c#
Based on your code and suggestions, I came up with one which supports folders extraction, which was one of my needs:
private async void UnZipFile(string file)
{
var folder = ApplicationData.Current.LocalFolder;
using (var zipStream = await folder.OpenStreamForReadAsync(file))
{
using (MemoryStream zipMemoryStream = new MemoryStream((int)zipStream.Length))
{
await zipStream.CopyToAsync(zipMemoryStream);
using (var archive = new ZipArchive(zipMemoryStream, ZipArchiveMode.Read))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.Name == "")
{
// Folder
await CreateRecursiveFolder(folder, entry);
}
else
{
// File
await ExtractFile(folder, entry);
}
}
}
}
}
}
private async Task CreateRecursiveFolder(StorageFolder folder, ZipArchiveEntry entry)
{
var steps = entry.FullName.Split('/').ToList();
steps.RemoveAt(steps.Count() - 1);
foreach (var i in steps)
{
await folder.CreateFolderAsync(i, CreationCollisionOption.OpenIfExists);
folder = await folder.GetFolderAsync(i);
}
}
private async Task ExtractFile(StorageFolder folder, ZipArchiveEntry entry)
{
var steps = entry.FullName.Split('/').ToList();
steps.RemoveAt(steps.Count() - 1);
foreach (var i in steps)
{
folder = await folder.GetFolderAsync(i);
}
using (Stream fileData = entry.Open())
{
StorageFile outputFile = await folder.CreateFileAsync(entry.Name, CreationCollisionOption.ReplaceExisting);
using (Stream outputFileStream = await outputFile.OpenStreamForWriteAsync())
{
await fileData.CopyToAsync(outputFileStream);
await outputFileStream.FlushAsync();
}
}
}