Fetch files from folder - c#

I have somes files in a folder. I want to fetch the files from that folder and convert each file to an object of binary stream and store in a collection. And from the collection, I want to retrieve each binary stream objects. How is it possible using ASP.Net with c# ?

It can be as simply as this:
using System;
using System.Collections.Generic;
using System.IO;
List<FileStream> files = new List<FileStream>();
foreach (string file in Directory.GetFiles("yourPath"))
{
files.Add(new FileStream(file, FileMode.Open, FileAccess.ReadWrite));
}
But overall, storing FileStreams like this does not sound like a good idea and does beg for trouble. Filehandles are a limited resource in any operating system, so hocking them is not very nice nore clever. You would be better off accessing the files as needed instead of simply keeping them open at a whim.
So basically storing only the paths and accessing the files as needed might be a better option.
using System;
using System.Collections.Generic;
using System.IO;
List<String> files = new List<String>();
foreach (string file in Directory.GetFiles("yourPath"))
{
files.Add(file);
}

If you wish to have it stored in a MemoryStream you could try
List<MemoryStream> list = new List<MemoryStream>();
string[] fileNames = Directory.GetFiles("Path");
for (int iFile = 0; iFile < fileNames.Length; iFile++)
{
using (FileStream fs = new FileStream(fileNames[iFile], FileMode.Open))
{
byte[] b = new byte[fs.Length];
fs.Read(b, 0, (int)fs.Length);
list.Add(new MemoryStream(b));
}
}
Or even use a Dictionary if you wish to keep the file names as keys
Dictionary<string, MemoryStream> files = new Dictionary<string, MemoryStream>();
string[] fileNames = Directory.GetFiles("Path");
for (int iFile = 0; iFile < fileNames.Length; iFile++)
{
using (FileStream fs = new FileStream(fileNames[iFile], FileMode.Open))
{
byte[] b = new byte[fs.Length];
fs.Read(b, 0, (int)fs.Length);
files.Add(Path.GetFileName(fileNames[iFile]), new MemoryStream(b));
}
}

This can be done using DirectoryInfo and FileInfo classes. Here is some code that should hopefully do what you need:
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(#"C:\TempDir\");
if (dir.Exists)
{
foreach (System.IO.FileInfo fi in dir.GetFiles())
{
System.IO.StreamReader sr = new System.IO.StreamReader(fi.OpenRead());
// do what you will....
sr.Close();
}
}

Related

how to make file content as base64 encoded before making zip file

I have a directory where I have CSV files which I need to first encode the file content as base64 string and then make it as zip file.
I am able to make file as zip with below code, but in between on the fly how to make file content as base64 encoded? Thanks!
var csvFiles = Directory.GetFiles(#"C:\Temp", "*.csv")
.Select(f => new FileInfo(f));
foreach (var file in csvFiles)
{
using (var newFile = ZipFile.Open($#"C:\tmp\{Path.GetFileNameWithoutExtension(file.Name)}.zip",
ZipArchiveMode.Create))
{
newFile.CreateEntryFromFile($#"C:\Temp\{file.Name}",
file.Name);
}
}
Disregarding your motives or other problems (conceptual or otherwise)
Here is a fully streamed solution with minimal allocations (let's be nice to your Large Object Heap). The CryptoStream with ToBase64Transform, is just a way to stream base64 encoding
var csvFiles = Directory.GetFiles(#"D:\Temp");
using var outputStream = new FileStream(#"D:\Test.zip", FileMode.Create);
using var archive = new ZipArchive(outputStream, ZipArchiveMode.Create, true);
foreach (var file in csvFiles)
{
using var inputFile = new FileStream(file, FileMode.Open, FileAccess.Read);
using var base64Stream = new CryptoStream(inputFile, new ToBase64Transform(), CryptoStreamMode.Read);
var entry = archive.CreateEntry(Path.GetFileName(file));
using var zipStream = entry.Open();
base64Stream.CopyTo(zipStream);
}
You need to create the base64 string, convert it to a byte array, and then create the archive entry from the byte array (by creating a stream).
Something like this should do the job:
var dirInfo = new DirectoryInfo(#"C:\Temp");
var csvFiles = dirInfo.GetFiles("*.csv"); // This already returns a `FileInfo[]`.
foreach (var file in csvFiles)
{
var fileBytes = File.ReadAllBytes(file.FullName);
var base64String = Convert.ToBase64String(fileBytes);
var base64Bytes = Encoding.UTF8.GetBytes(base64String);
string newFilePath = $#"C:\tmp\{Path.GetFileNameWithoutExtension(file.Name)}.zip";
using (var newFile = ZipFile.Open(newFilePath, ZipArchiveMode.Create))
{
// You might want to change the extension
// since the file is no longer in CSV format.
var zipEntry = newFile.CreateEntry(file.Name);
using (var base64Stream = new MemoryStream(base64Bytes))
using (var zipEntryStream = zipEntry.Open())
{
base64Stream.CopyTo(zipEntryStream);
}
}
}
Alternatively, you could save the base64 string to a temporary file, create the entry from that file, and then delete it; but I don't prefer writing dummy data to the disk when the job can be done in memory.

SharpZipLib compression level between creating or updating a zip file

I've got a couple of questions regarding the use of ShareZipLib.
I'm using 2 different methods to create a zip file and updating one which seems like overkill. Is there a better way?
Create Zip File:
using (var zipFile = File.Open(zipFilename, FileMode.CreateNew))
using (var outputStream = new ZipOutputStream(zipFile))
{
outputStream.Password = password;
outputStream.SetLevel(compressionLevel); // 0 - store only to 9 - means best compression
var buffer = new byte[4096];
foreach (var file in filenames)
{
var entry = new ZipEntry(Path.GetFileName(file));
entry.DateTime = DateTime.Now;
outputStream.PutNextEntry(entry);
using (var fs = File.OpenRead(file))
{
var sourceBytes = 0;
do
{
sourceBytes = fs.Read(buffer, 0, buffer.Length);
outputStream.Write(buffer, 0, sourceBytes);
} while (sourceBytes > 0);
}
}
outputStream.Finish();
outputStream.Close();
}
Update ZipFile:
using (var zipFile = File.Open(zipFilename, FileMode.Append))
using (var outputStream = new ZipFile(zipFile))
{
outputStream.Password = password;
outputStream.BeginUpdate();
foreach (var filename in filenames)
{
var entry = new ZipEntry(Path.GetFileName(filename));
entry.DateTime = DateTime.Now;
outputStream.Add(entry);
}
outputStream.CommitUpdate();
}
I've tried using the File.Open(zipFilename, FileMode.Append) on the create method hoping I'd be able to append files to the zip file but it just overrides it. The only other way I found is the second method but as I said, it seems like an overkill. Hopefully, this can be simplified and this brings my second question/issue.
Why is the compression level set at the stream level, which makes sense to me by the way, when creating a new zip file but when you are updating a zip file, you have to set it at the file level.
Also when setting it at the stream level, it requires a number from 0 to 9.
outputStream.SetLevel(compressionLevel);
But when setting it at the file level on the update, it uses an enum:
Surely, you don't want to have different level of compression when adding and updating but 0 to 9 are not the values provided in the enum.
Thanks.

How to go from byte[], to MemoryStream, Unzip, then write to FileStream

I am unsure what I am doing wrong. The files that I create after grabbing a byte[] (which is emailAttachment.Body) and passing it to the method ExtractZipFile, converting it to MemoryStream and then unzipping it, returning it as a KeyValuePair and then Writing to a file using FileStream.
However when I go to open the new created files there is an error in opening them. They are not able to be opened.
The below are in the same class
using Ionic.Zip;
var extractedFiles = ExtractZipFile(emailAttachment.Body);
foreach (KeyValuePair<string, MemoryStream> extractedFile in extractedFiles)
{
string FileName = extractedFile.Key;
using (FileStream file = new FileStream(CurrentFileSystem +
FileName.FileFullPath(),FileMode.Create, System.IO.FileAccess.Write))
{
byte[] bytes = new byte[extractedFile.Value.Length];
extractedFile.Value.Read(bytes, 0, (int) xtractedFile.Value.Length);
file.Write(bytes,0,bytes.Length);
extractedFile.Value.Close();
}
}
private Dictionary<string, MemoryStream> ExtractZipFile(byte[] messagePart)
{
Dictionary<string, MemoryStream> result = new Dictionary<string,MemoryStream>();
MemoryStream data = new MemoryStream(messagePart);
using (ZipFile zip = ZipFile.Read(data))
{
foreach (ZipEntry ent in zip)
{
MemoryStream memoryStream = new MemoryStream();
ent.Extract(memoryStream);
result.Add(ent.FileName,memoryStream);
}
}
return result;
}
Is there something I am missing? I do not want to save the original zip file just the extracted Files from MemoryStream.
What am I doing wrong?
After writing to your MemoryStream, you're not setting the position back to 0:
MemoryStream memoryStream = new MemoryStream();
ent.Extract(memoryStream);
result.Add(ent.FileName,memoryStream);
Because of this, the stream position will be at the end when you try to read from it, and you'll read nothing. Make sure to rewind it:
memoryStream.Position = 0;
Also, you don't have to handle the copy manually. Just let the CopyTo method take care of it:
extractedFile.Value.CopyTo(file);
I'd suggest that you clean up your use of MemoryStream in your code.
I agree that calling memoryStream.Position = 0; will allow this code to work correctly, but it's an easy thing to miss when reading and writing memory streams.
It's better to write code that avoids the bug.
Try this:
private IEnumerable<(string Path, byte[] Content)> ExtractZipFile(byte[] messagePart)
{
using (var data = new MemoryStream(messagePart))
{
using (var zipFile = ZipFile.Read(data))
{
foreach (var zipEntry in zipFile)
{
using (var memoryStream = new MemoryStream())
{
zipEntry.Extract(memoryStream);
yield return (Path: zipEntry.FileName, Content: memoryStream.ToArray());
}
}
}
}
}
Then your calling code would look something like this:
foreach (var extractedFile in ExtractZipFile(emailAttachment.Body))
{
File.WriteAllBytes(Path.Combine(CurrentFileSystem, extractedFile.Path.FileFullPath()), extractedFile.Content);
}
It's just a lot less code and a much better chance of avoiding bugs. The number one predictor of bugs in code is the number of lines of code you write.
Since I find it all a lot of code for a simple operation, here's my two cents.
using Ionic.Zip;
using (var s = new MemoryStream(emailAttachment.Body))
using (ZipFile zip = ZipFile.Read(s))
{
foreach (ZipEntry ent in zip)
{
string path = Path.Combine(CurrentFileSystem, ent.FileName.FileFullPath())
using (FileStream file = new FileStream(path, FileAccess.Write))
{
ent.Extract(file);
}
}
}

How to compress multiple files in zip file

I'm trying to compress two text files to a zip file. This is how my public method looks like:
public ActionResult Index()
{
byte[] file1 = System.IO.File.ReadAllBytes(#"C:\file1.txt");
byte[] file2 = System.IO.File.ReadAllBytes(#"C:\file2.txt");
Dictionary<string, byte[]> fileList = new Dictionary<string, byte[]>();
fileList.Add("file1.txt", file1);
fileList.Add("file2.txt", file2);
CompressToZip("zip.zip", fileList);
return View();
}
This is how my compress method looks like:
private void CompressToZip(string fileName, Dictionary<string, byte[]> fileList)
{
using (var memoryStream = new MemoryStream())
{
foreach (var file in fileList)
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var demoFile = archive.CreateEntry(file.Key);
using (var entryStream = demoFile.Open())
using (var b = new BinaryWriter(entryStream))
{
b.Write(file.Value);
}
}
}
using (var fileStream = new FileStream(fileName, FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
}
In this approach, the zip folder is perfectly created. However the issue is I'm getting only one file inside the zip folder (Just the second file will be created inside the zip folder).
There are no errors found.
Question: How to compress both text files into the zip folder?
Thank you in advanced!
Your code is actually saving two separate zip archives to the zip.zip file (a new ZipArchive is created for each file to be compressed). The first zip archive contains only file1.txt, the second only file2.txt. When you open zip.zip in Windows Explorer, it shows just the contents of the second zip archive.
To create a single zip archive containing both files, just move the creation of the ZipArchive outside of your fileList loop:
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach (var file in fileList)
{
var demoFile = archive.CreateEntry(file.Key);
using (var entryStream = demoFile.Open())
using (var b = new BinaryWriter(entryStream))
{
b.Write(file.Value);
}
}
}
At first glance, I would suggest that your foreach statement around the using var (archive = new ZipArchive...) is the wrong way round.
This way you are creating a new ZipArchive each time you iterate the foreach loop.
Surely you want to create the ZipArchive and loop through the foreach inside of that?
Like this:
private void CompressToZip(string fileName, Dictionary<string, byte[]> fileList)
{
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach (var file in fileList)
{
var demoFile = archive.CreateEntry(file.Key);
using (var entryStream = demoFile.Open())
using (var b = new BinaryWriter(entryStream))
{
b.Write(file.Value);
}
}
}
using (var fileStream = new FileStream(fileName, FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
}
Hope this helps!
string startPath = #"c:\example\start";
string zipPath = #"c:\example\result.zip";
ZipFile.CreateFromDirectory(startPath, zipPath);

Nesting Zip Files and Folders in Memory using DotNetZip Library

We have a page that users can download media and we construct a folder structure similar to the following and zip it up and send it back to the user in the response.
ZippedFolder.zip
- Folder A
- File 1
- File 2
- Folder B
- File 3
- File 4
The existing implementation that accomplishes this saves files and directories temporarily to file system and then deletes them at the end. We are trying to get away from doing this and would like to accomplish this entirely in memory.
I am able to successfully create a ZipFile with files in it, but the problem I am running into is creating Folder A and Folder B and adding files to those and then adding those two folders to the Zip File.
How can I do this without saving to the file system?
The code for just saving the file streams to the zip file and then setting the Output Stream on the response is the following.
public Stream CompressStreams(IList<Stream> Streams, IList<string> StreamNames, Stream OutputStream = null)
{
MemoryStream Response = null;
using (ZipFile ZippedFile = new ZipFile())
{
for (int i = 0, length = Streams.Count; i < length; i++)
{
ZippedFile.AddEntry(StreamNames[i], Streams[i]);
}
if (OutputStream != null)
{
ZippedFile.Save(OutputStream);
}
else
{
Response = new MemoryStream();
ZippedFile.Save(Response);
// Move the stream back to the beginning for reading
Response.Seek(0, SeekOrigin.Begin);
}
}
return Response;
}
EDIT We are using DotNetZip for the zipping/unzipping library.
Here's another way of doing it using System.IO.Compression.ZipArchive
public Stream CompressStreams(IList<Stream> Streams, IList<string> StreamNames, Stream OutputStream = null)
{
MemoryStream Response = new MemoryStream();
using (ZipArchive ZippedFile = new ZipArchive(Response, ZipArchiveMode.Create, true))
{
for (int i = 0, length = Streams.Count; i < length; i++)
using (var entry = ZippedFile.CreateEntry(StreamNames[i]).Open())
{
Streams[i].CopyTo(entry);
}
}
if (OutputStream != null)
{
Response.Seek(0, SeekOrigin.Begin);
Response.CopyTo(OutputStream);
}
return Response;
}
and a little test:
using (var write = new FileStream(#"C:\users\Public\Desktop\Testzip.zip", FileMode.OpenOrCreate, FileAccess.Write))
using (var read = new FileStream(#"C:\windows\System32\drivers\etc\hosts", FileMode.Open, FileAccess.Read))
{
CompressStreams(new List<Stream>() { read }, new List<string>() { #"A\One.txt" }, write);
}
re: your comment -- sorry, not sure if it creates something in the background, but you're not creating it yourself to do anything

Categories