Сreating a file in memory - c#

There is a question.
I need to put a separate ini file into the archive.
BUT I don't want to collect and store this file on disk.
Is it possible to collect a file and place it in an archive without saving it to disk?
Now the archive is going like this.
var memoryStream = new MemoryStream();
var response = new HttpResponseMessage(HttpStatusCode.OK);
var filepaths = Directory.GetFiles(setting.DirMicroSip);
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach(string filepath in filepaths)
{
string filename = Path.GetFileName(filepath);
var entry = archive.CreateEntry(filename);
using (var file = File.OpenRead(Path.Combine(setting.DirMicroSip, filename)))
using (var entryStream = entry.Open())
{
await file.CopyToAsync(entryStream);
}
}
}
memoryStream.Position = 0;
response.Content = new StreamContent(memoryStream);
response.Content.Headers.ContentLength = memoryStream.Length;
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "Archive.zip"
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
return response;
And ini on the disk like this.
var ini = new IniFile("C:/arh/MicroSIP_test.ini");
ini.Write("accountId", "1", "Settings");
ini.Write("singleMode", "1", "Settings");...
Help me please

Related

Download multiple files .NET CORE Web API as ArchiveZip return root directory

I make a private class to get the bytes of the file and then return it into a zip file. Basically, I just want to pick specific files inside my folder and then zip it then download it. Here's my class:
private FileResult DownloadMultipleFiles(List<byte[]> byteArrayList)
{
var zipName = $"archive-EvidenceFiles-{DateTime.Now.ToString("yyyy_MM_dd-HH_mm_ss")}.zip";
using (MemoryStream ms = new MemoryStream())
{
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
foreach (var file in byteArrayList)
{
string fPath = Encoding.ASCII.GetString(file);
var entry = archive.CreateEntry(fPath, CompressionLevel.Fastest);
using (var zipStream = entry.Open())
{
zipStream.Write(file, 0, file.Length);
}
}
}
return File(ms.ToArray(), "application/zip", zipName);
}
}
And then, here's my controller:
[HttpGet("GetBundleFiles/{rhaId}")]
public async Task<IActionResult> GetBundleFiles(string rhaId)
{
List<byte[]> filesPath = new List<byte[]>();
var results = await _rhaFileEvidence.GetByRhaID(rhaId);
var files = results.ToList();
if (files.Count == 0)
return Ok(new { status = "null", message = "Empty data" });
files.ForEach(file =>
{
var fPath = file.FilePath;
byte[] bytes = Encoding.ASCII.GetBytes(fPath);
filesPath.Add(bytes);
});
return DownloadMultipleFiles(filesPath);
}
The controller works well, I can download the zip but when I open it, I can't get the files instead I get the root directory of the project I saved, like D:. I think I make mistake when making the memory stream or something, is there any suggestion how can I fix this? (Paste some solution code in the answer please)
you are getting the directory root because when you use archive.CreateEntry you are passing the file full path in parameter, you should be using only the file name
var entry = archive.CreateEntry(System.IO.Path.GetFileName(fPath), CompressionLevel.Fastest);
a second issue is that you actually saving the file path to your files not the content of the original file. you can update your DownloadMultipleFiles like this
private FileResult DownloadMultipleFiles(List<byte[]> byteArrayList)
{
var zipName = $"archive-EvidenceFiles-{DateTime.Now.ToString("yyyy_MM_dd-HH_mm_ss")}.zip";
using (MemoryStream ms = new MemoryStream())
{
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
foreach (var file in byteArrayList)
{
string fPath = Encoding.ASCII.GetString(file);
var entry = archive.CreateEntry(System.IO.Path.GetFileName(fPath), CompressionLevel.Fastest);
using (var zipStream = entry.Open())
{
var bytes = System.IO.File.ReadAllBytes(fPath);
zipStream.Write(bytes, 0, bytes.Length);
}
}
}
return File(ms.ToArray(), "application/zip", zipName);
}
}

zip multiple pdfs from url link, how to

I have a project that requires pdf files to be zipped up from an URL link and then downloaded and clickable by the end users browser. So far, I was able to zip one pdf file which isn't nearly what I'm looking to for.
I'm not sure how to proceed from here. Below is the code. Any help would be very much appreciated.
ASP.NET Core
[HttpGet("zipFiles")]
public IActionResult ZipPDFFiles()
{
var fileNames = _repo.GetFileNames();
foreach (var filesName in fileNames)
{
var urlLink = "https://example.com/folder/" + $"{filesName.PdfFileName}";
var net = new System.Net.WebClient();
var data = net.DownloadData(urlLink);
var file = $"{filesName.PdfFileName}";
var contentType = "application/zip";
string zippedFolderName = "Archive.zip";
using (MemoryStream ms = new MemoryStream())
{
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
var zipArchiveEntry = archive.CreateEntry($"{file}", System.IO.Compression.CompressionLevel.Fastest);
using (var zipStream = zipArchiveEntry.Open()) zipStream.Write(data, 0, data.Length);
}
return File(ms.ToArray(), contentType, $"{zippedFolderName}");
}
}
return NotFound();
}
Some improvements:
used HttpClient
files uploaded simultaneously
var fileUrls = new[]
{
new Uri("https://learn.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/media/image3.5.png"),
new Uri("https://learn.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/media/image4.png"),
new Uri("https://learn.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/media/image6.png")
};
var downloadResults = fileUrls
.Select(uri => (uri: uri, response: HttpClientFactory.Create().SendAsync(new HttpRequestMessage(HttpMethod.Get, uri))))
.ToArray();
await Task.WhenAll(downloadResults.Select(v => v.response));
using (var ms = new MemoryStream())
{
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
foreach (var download in downloadResults)
{
var entry = archive.CreateEntry(download.uri.Segments.Last(), CompressionLevel.Fastest);
using (var zipStream = entry.Open())
{
var data = await download.response.Result.Content.ReadAsByteArrayAsync();
zipStream.Write(data, 0, data.Length);
}
}
}
return File(ms.ToArray(), contentType, $"{zippedFolderName}");
}

C# ZipArchive - How to nest internal .zip files without writing to disk

I need to create a zip file in memory, then send the zip file to the client. However, there are cases where the created zip file will need to contain other zip files that were also generated in memory. For instance, the file structure might look like this:
SendToClient.zip
InnerZip1.zip
File1.xml
File2.xml
InnerZip2.zip
File3.xml
File4.xml
I've been attempting to use the System.IO.Compression.ZipArchive library. I cannot use the System.IO.Compression.ZipFile library because my project's version of .NET is not compatible with it.
Here's an example of what I've tried.
public Stream GetMemoryStream() {
var memoryStream = new MemoryStream();
string fileContents = "Lorem ipsum dolor sit amet";
string entryName = "Lorem.txt";
string innerZipName = "InnerZip.zip";
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
ZipArchiveEntry entry = archive.CreateEntry(Path.Combine(innerZipName, entryName), CompressionLevel.Optimal);
using (var writer = new StreamWriter(entry.Open())) {
writer.Write(fileContents);
}
}
return memoryStream
}
However, this just puts Lorem.txt in a folder called "Inner.zip" (instead of in an actual zip file).
I can create an empty inner zip file if I create an entry called "Inner.zip" without writing to it. I can't add anything to it, though, and writing to an entry called "Inner.zip\Lorem.txt" afterward just creates a folder again (alongside the identically named empty .zip file).
I've also tried creating a separate archive, serializing it with a memory stream, then writing that to the original archive as a .zip.
public Stream CreateOuterZip() {
var memoryStream = new MemoryStream();
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
ZipArchiveEntry entry = archive.CreateEntry("Outer.zip", CompressionLevel.NoCompression);
using (var writer = new BinaryWriter(entry.Open())) {
writer.Write(GetMemoryStream().ToArray());
}
}
return memoryStream;
}
This just creates an invalid .zip file that windows doesn't know how to open, though.
Thanks in advance!
So I created a FileStream instead of a MemoryStream so the code can be tested easier
public static Stream CreateOuterZip()
{
string fileContents = "Lorem ipsum dolor sit amet";
// Final zip file
var fs = new FileStream(
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SendToClient.zip"), FileMode.OpenOrCreate);
// Create inner zip 1
var innerZip1 = new MemoryStream();
using (var archive = new ZipArchive(innerZip1, ZipArchiveMode.Create, true))
{
var file1 = archive.CreateEntry("File1.xml");
using (var writer = new BinaryWriter(file1.Open()))
{
writer.Write(fileContents); // Change fileContents to real XML content
}
var file2 = archive.CreateEntry("File2.xml");
using (var writer = new BinaryWriter(file2.Open()))
{
writer.Write(fileContents); // Change fileContents to real XML content
}
}
// Create inner zip 2
var innerZip2 = new MemoryStream();
using (var archive = new ZipArchive(innerZip2, ZipArchiveMode.Create, true))
{
var file3 = archive.CreateEntry("File3.xml");
using (var writer = new BinaryWriter(file3.Open()))
{
writer.Write(fileContents); // Change fileContents to real XML content
}
var file4 = archive.CreateEntry("File4.xml");
using (var writer = new BinaryWriter(file4.Open()))
{
writer.Write(fileContents); // Change fileContents to real XML content
}
}
using (var archive = new ZipArchive(fs, ZipArchiveMode.Create, true))
{
// Create inner zip 1
var innerZipEntry = archive.CreateEntry("InnerZip1.zip");
innerZip1.Position = 0;
using (var s = innerZipEntry.Open())
{
innerZip1.WriteTo(s);
}
// Create inner zip 2
var innerZipEntry2 = archive.CreateEntry("InnerZip2.zip");
innerZip2.Position = 0;
using (var s = innerZipEntry2.Open())
{
innerZip2.WriteTo(s);
}
}
fs.Close();
return fs; // The file is written, can probably just close this
}
You can obviously modify this method to return a MemoryStream, or change the method to Void to just have the zip file written out to disk
You should create ZipArchive for internal zip file also. Write it to stream (memorystream). And after write this stream as general stream into main zip.
static Stream Inner() {
var memoryStream = new MemoryStream();
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
var demoFile = archive.CreateEntry("foo2.txt");
using (var entryStream = demoFile.Open())
using (var streamWriter = new StreamWriter(entryStream)) {
streamWriter.Write("Bar2!");
}
}
return memoryStream;
}
static void Main(string[] args) {
using (var memoryStream = new MemoryStream()) {
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
var demoFile = archive.CreateEntry("foo.txt");
using (var entryStream = demoFile.Open())
using (var streamWriter = new StreamWriter(entryStream)) {
streamWriter.Write("Bar!");
}
var zip = archive.CreateEntry("inner.zip");
using (var entryStream = zip.Open()) {
var inner = Inner();
inner.Seek(0, SeekOrigin.Begin);
inner.CopyTo(entryStream);
}
}
using (var fileStream = new FileStream(#"d:\test.zip", FileMode.Create)) {
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
Thanks to this answer.

How to convert GZipStream to HttpContent?

I have written a Web API code to return a zip file. But I am not able to convert the GZipStream content to HttpContent. I get the following error:
cannot implicitly convert type 'system.io.compression.GZipStream' to
'System.Net.Http.HttpContent'
Where did I go wrong?
My WebApi Code:
var content =
new GZipStream(memStream, CompressionMode.Compress);
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
httpResponseMessage.Content = content;
httpResponseMessage.Content.Headers.Add("x-filename", document.FileName);
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentDisposition.FileName = "xyz.zip";
httpResponseMessage.StatusCode = HttpStatusCode.OK;
I got a solution using Zip archive and I have created a static class to push the memory stream into a zip archive as below,
var pushStreamContent = ZipStreamContent.Create("MultipleDocument.zip", memStList);
ZipStreamContent class,
public static class ZipStreamContent
{
public static PushStreamContent Create(string fileName, List<MemoryStream> msList)
{
var content = new PushStreamContent((outputStream, httpContent, transportContext) =>
{
using (var zip = new ZipArchive(outputStream, ZipArchiveMode.Create, leaveOpen: false))
{
msList[0].Position = 0;
var createenter = zip.CreateEntry("xyz.jpg", CompressionLevel.Optimal);
using (var s = createenter.Open())
{
msList[0].CopyTo(s);
}
}
});
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
content.Headers.ContentDisposition.FileName = fileName;
return content;
}
}
I have simplified the above given GipStreamContent static class like following, It's working well, so I hope It will help all others.
CloudBlockBlob blob = null;
//azure storage connection
var container = GetBlobClient(tenantInfo);
//directory reference
var directory = container.GetDirectoryReference(
string.Format(DirectoryNameConfigValue, tenantInfo.TenantId.ToString(), documentList[0].ProjectId));
var pushStreamContent = new PushStreamContent(async (outputStream, httpContent, transportContext) =>
{
//zip the multiple files
using (var zipEntry = new ZipArchive(outputStream, ZipArchiveMode.Create, leaveOpen: false))
{
for (int docId = 0; docId < documentList.Count; docId++)
{
blob = directory.GetBlockBlobReference(DocumentNameConfigValue + documentList[docId].DocumentId);
if (!blob.Exists()) continue;
MemoryStream memStream = new MemoryStream();
await blob.DownloadToStreamAsync(memStream);
memStream.Position = 0;
var createEntry = zipEntry.CreateEntry(documentList[docId].FileName, CompressionLevel.Fastest);
using (var stream = createEntry.Open())
{
memStream.CopyTo(stream);
}
}
}
});

How to send a zip file from Web API 2 HttpGet

I'm trying to figure it out how to create new zip file from a given folder path, and sending it back to the sender.
The need is that the file will be downloaded at the sender that requested it.
I've seen alot of answers but none helped me with the exact answer.
My code:
Guid folderGuid = Guid.NewGuid();
string folderToZip = ConfigurationManager.AppSettings["folderToZip"] + folderGuid.ToString();
Directory.CreateDirectory(folderToZip);
string directoryPath = ConfigurationManager.AppSettings["DirectoryPath"];
string combinedPath = Path.Combine(directoryPath, id);
DirectoryInfo di = new DirectoryInfo(combinedPath);
if (di.Exists)
{
//provide the path and name for the zip file to create
string zipFile = folderToZip + "\" + folderGuid + ".zip";
//call the ZipFile.CreateFromDirectory() method
ZipFile.CreateFromDirectory(combinedPath, zipFile, CompressionLevel.Fastest, true);
var result = new HttpResponseMessage(HttpStatusCode.OK);
using (ZipArchive zip = ZipFile.Open(zipFile, ZipArchiveMode.Read))
{
zip.CreateEntryFromFile(folderFiles, "file.zip");
}
var stream = new FileStream(zipFile, FileMode.Open);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "file.zip"
};
log.Debug("END ExportFiles()");
return ResponseMessage(result);
In your controller:
using System.IO.Compression.FileSystem; // Reference System.IO.Compression.FileSystem.dll
[HttpGet]
[Route("api/myzipfile"]
public dynamic DownloadZip([FromUri]string dirPath)
{
if(!System.IO.Directory.Exists(dirPath))
return this.NotFound();
var tempFile = System.IO.Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid()); // might want to clean this up if there are a lot of downloads
ZipFile.CreateFromDirectory(dirPath, tempFile);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(new FileStream(tempFile, FileMode.Open, FileAccess.Read));
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = fileName;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
return response;
}
UPD: Workaround the File Already Exists

Categories