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");
}
}
}
}
Related
I created the functionality to get documents from blob storage and then add them to a zip file for download.
[HttpPost]
public FileContentResult DownloadDocumentsByDocIDZIP(List<int> documentIDs)
{
List<Document> docs = new List<Document>();
foreach (int doc in documentIDs)
{
if (doc != 0)
{
Document document = documentService.GetDocumentByID(doc, false);
docs.Add(document);
}
}
MemoryStream outms = new MemoryStream();
using (ZipArchive zar = new ZipArchive(outms, ZipArchiveMode.Create, false))
{
foreach (Document docu in docs)
{
if (docu != null)
{
byte[] documentdata = documentService.DownloadDocumentData(docu.DocumentID);
string name = docu.DocumentNiceName ?? docu.DocumentFileName;
byte[] unzipped = documentdata;
ZipArchiveEntry entry = zar.CreateEntry(name);
Stream str = entry.Open();
MemoryStream ms = new MemoryStream(unzipped);
ms.CopyTo(str);
}
}
outms.Seek(0, SeekOrigin.Begin);
}
var outdata = outms.ToArray();
var result = File(outdata, "application/zip", "documents.zip");
return result;
}
When I hit the function via ajax, It fails at
ZipArchiveEntry entry = zar.CreateEntry(name);
I'm given the exception,
System.IO.IOException: 'Entries cannot be created while previously created entries are still open.'
So I added str.close()
using (ZipArchive zar = new ZipArchive(outms, ZipArchiveMode.Create, false))
{
foreach (Document docu in docs)
{
if (docu != null)
{
byte[] documentdata = documentService.DownloadDocumentData(docu.DocumentID);
string name = docu.DocumentNiceName ?? docu.DocumentFileName;
byte[] unzipped = documentdata;
ZipArchiveEntry entry = zar.CreateEntry(name);
Stream str = entry.Open();
MemoryStream ms = new MemoryStream(unzipped);
ms.CopyTo(str);
str.Close();
}
}
outms.Seek(0, SeekOrigin.Begin);
}
var outdata = outms.ToArray();
var result = File(outdata, "application/zip", "documents.zip");
return result;
Now it creates the file but when you try to unzip it after download.
It gives me an error in WinZip. Error: unable to seek to beginning of Central Directory.
Can someone please assist I have no idea what I'm doing wrong?
you have to dispose the Stream before add new stream to zip but the real problem is that you call Seek on stream, try the following code:
using (ZipArchive zar = new ZipArchive(outms, ZipArchiveMode.Create, false))
{
foreach (Document docu in docs)
{
if (docu != null)
{
byte[] documentdata = documentService.DownloadDocumentData(docu.DocumentID);
string name = docu.DocumentNiceName ?? docu.DocumentFileName;
byte[] unzipped = documentdata;
ZipArchiveEntry entry = zar.CreateEntry(name);
using (Stream str = entry.Open())
{
str.Write(unzipped);
}
}
}
//outms.Seek(0, SeekOrigin.Begin); //This causes "Error: unable to seek to beginning of Central Directory."
}
var outdata = outms.ToArray();
var result = File(outdata, "application/zip", "documents.zip");
return result;
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);
}
}
}
I am trying to compress several excel files, but when generate the excel it generates corrupt. I don't know what could be any idea?
var Clients = getClients();
var carpetaUrl = string.Format(#"{0}/{1}/{2}.zip", IdCuenta, IdListado, "Folder");
try
{
using (MemoryStream zipFile = new MemoryStream())
{
using (var archive = new Archive())
{
foreach (var item in Clients)
{
var (fileUrl, stream) = GenerateFileExcel(item.archivoNombre,item.IdClient);
archive.CreateEntry(fileUrl, stream);
}
archive.Save(zipFile, new ArchiveSaveOptions() { Encoding = Encoding.ASCII });
}
var uri = B.Azure.Storages.UploadFile("tttt", zipFile, carpetaUrl, false).ToString();
return uri;
}
I made myself some code that is supposed to make a Zip file containing another file. This file is created from a canvas. But the zip file that is created is empty and doesn't contain anything. The exception also isn't thrown. Any idea what might be wrong?
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
savePicker.FileTypeChoices.Add("Extended sketchpad", new[] { ".exsk" });
Windows.Storage.StorageFile file = await savePicker.PickSaveFileAsync();
if (null != file)
{
try
{
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var giffile = archive.CreateEntry("layer.gif");
using (Stream entryStream = giffile.Open())
{
await MyInkCanvas.InkPresenter.StrokeContainer.SaveAsync(entryStream.AsOutputStream());
}
}
using (Windows.Storage.Streams.IRandomAccessStream finalStream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
{
finalStream.Size = 0;
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(finalStream.AsStreamForWrite());
}
}
//MainPage.NotifyUser("File has been saved!", NotifyType.StatusMessage);
}
catch (Exception ex)
{
//MainPage.NotifyUser(ex.Message, NotifyType.ErrorMessage);
}
}
Why not write to file directly?
using (var fileStream = await file.OpenStreamForWriteAsync())
{
using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true))
{
var giffile = archive.CreateEntry("layer.gif");
using (Stream entryStream = giffile.Open())
{
await MyInkCanvas.InkPresenter.StrokeContainer.SaveAsync(entryStream.AsOutputStream());
}
}
}
I am using ZipArchive to create a zipped folder for a list of documents.
I cant figure out why, when I return my archived folder it is empty.
Does anyone see what I am doing wrong here?
My code is as follows:
if (files.Count > 1)
{
var ms = new MemoryStream();
var zipArchive = new ZipArchive(ms, ZipArchiveMode.Create, false);
foreach (var file in files)
{
var entry = zipArchive.CreateEntry(file.UploadFileName, CompressionLevel.Fastest);
using (var streamWriter = new StreamWriter(entry.Open()))
{
Stream strFile = new MemoryStream(file.UploadFileBytesStream);
streamWriter.Write(strFile);
strFile.CopyTo(ms);
}
}
return File(ms, System.Net.Mime.MediaTypeNames.Application.Zip, "FinancialActivityReports.zip");
}
Assuming the following model for file
public class FileModel {
public string UploadFileName { get; set; }
public byte[] UploadFileBytesStream { get; set; }
}
The following helper was written to create the stream of the compressed files
public static class FileModelCompression {
public static Stream Compress(this IEnumerable<FileModel> files) {
if (files.Any()) {
var ms = new MemoryStream();
var archive = new ZipArchive(ms, ZipArchiveMode.Create, false);
foreach (var file in files) {
var entry = archive.add(file);
}
ms.Position = 0;
return ms;
}
return null;
}
private static ZipArchiveEntry add(this ZipArchive archive, FileModel file) {
var entry = archive.CreateEntry(file.UploadFileName, CompressionLevel.Fastest);
using (var stream = entry.Open()) {
stream.Write(file.UploadFileBytesStream, 0, file.UploadFileBytesStream.Length);
stream.Position = 0;
stream.Close();
}
return entry;
}
}
You code, assuming files is derived from IEnumerable<FileModel> will then change to this...
if (files.Count > 1)
{
var stream = files.Compress();
return File(stream, System.Net.Mime.MediaTypeNames.Application.Zip, "FinancialActivityReports.zip");
}