Read the content of an xml file within a zip package - c#

I am required to read the contents of an .xml file using the Stream (Here the xml file is existing with in the zip package). Here in the below code, I need to get the file path at runtime (here I have hardcoded the path for reference). Please let me know how to read the file path at run time.
I have tried to use string s =entry.FullName.ToString(); but get the error "Could not find the Path". I have also tried to hard code the path as shown below. however get the same FileNotFound error.
string metaDataContents;
using (var zipStream = new FileStream(#"C:\OB10LinuxShare\TEST1\Temp" + "\\"+zipFileName+".zip", FileMode.Open))
using (var archive = new ZipArchive(zipStream, ZipArchiveMode.Read))
{
foreach (var entry in archive.Entries)
{
if (entry.Name.EndsWith(".xml"))
{
FileInfo metadataFileInfo = new FileInfo(entry.Name);
string metadataFileName = metadataFileInfo.Name.Replace(metadataFileInfo.Extension, String.Empty);
if (String.Compare(zipFileName, metadataFileName, true) == 0)
{
using (var stream = entry.Open())
using (var reader = new StreamReader(stream))
{
metaDataContents = reader.ReadToEnd();
clientProcessLogWriter.WriteToLog(LogWriter.LogLevel.DEBUG, "metaDataContents : " + metaDataContents);
}
}
}
}
}
I have also tried to get the contents of the .xml file using the Stream object as shown below. But here I get the error "Stream was not readable".
Stream metaDataStream = null;
string metaDataContent = string.Empty;
using (Stream stream = entry.Open())
{
metaDataStream = stream;
}
using (var reader = new StreamReader(metaDataStream))
{
metaDataContent = reader.ReadToEnd();
}
Kindly suggest, how to read the contents of the xml with in a zip file using Stream and StreamReader by specifying the file path at run time

Your section code snippet is failing because when you reach the end of the first using statement:
using (Stream stream = entry.Open())
{
metaDataStream = stream;
}
... the stream will be disposed. That's the point of a using statment. You should be fine with this sort of code, but load the XML file while the stream is open:
XDocument doc;
using (Stream stream = entry.Open())
{
doc = XDocument.Load(stream);
}
That's to load it as XML... if you really just want the text, you could use:
string text;
using (Stream stream = entry.Open())
{
using (StreamReader reader = new StreamReader(stream))
{
text = reader.ReadToEnd();
}
}
Again, note how this is reading before it hits the end of either using statement.

Here is a sample of how to read a zip file using .net 4.5
private void readZipFile(String filePath)
{
String fileContents = "";
try
{
if (System.IO.File.Exists(filePath))
{
System.IO.Compression.ZipArchive apcZipFile = System.IO.Compression.ZipFile.Open(filePath, System.IO.Compression.ZipArchiveMode.Read);
foreach (System.IO.Compression.ZipArchiveEntry entry in apcZipFile.Entries)
{
if (entry.Name.ToUpper().EndsWith(".XML"))
{
System.IO.Compression.ZipArchiveEntry zipEntry = apcZipFile.GetEntry(entry.Name);
using (System.IO.StreamReader sr = new System.IO.StreamReader(zipEntry.Open()))
{
//read the contents into a string
fileContents = sr.ReadToEnd();
}
}
}
}
}
catch (Exception)
{
throw;
}
}

Related

Returning a zipfile from a web api

I have built an asp net web api. I need to return a zipfile, as a result of some inner logic. I'm using this code and it works, but the resulting zip file, when unzipped manually, gave me this error "There are data after the end of the payload"
using (ZipFile zip = new ZipFile())
{
...
zip.Save(di.FullName + "\\" + "Update.zip");
}
string path = Path.Combine(Properties.Settings.Default.PathDisposizioniHTML, "Update.zip");
var response = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new System.IO.FileStream(path, System.IO.FileMode.Open);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
This is how i receive the data in a .net console application:
using (Stream output = File.OpenWrite(#"C:\prova\MyFile.zip"))
using (Stream input = httpResponse.GetResponseStream())
{
input.CopyTo(output);
}
If you already have the zip file on your system, you shouldn't need to do anything special before sending it as a response.
This should work:
string filePath = #"C:\myfolder\myfile.zip";
return File(filePath, "application/zip");
If you're making the file on the fly, i.e. getting other files and programatically putting them into a zip file for the user, the following should work:
public IActionResult GetZipFile(){
//location of the file you want to compress
string filePath = #"C:\myfolder\myfile.ext";
//name of the zip file you will be creating
string zipFileName = "zipFile.zip";
byte[] result;
using (MemoryStream zipArchiveMemoryStream = new MemoryStream())
{
using (ZipArchive zipArchive = new ZipArchive(zipArchiveMemoryStream, ZipArchiveMode.Create, true))
{
ZipArchiveEntry zipEntry = zipArchive.CreateEntry(zipFileName);
using (Stream entryStream = zipEntry.Open())
{
using (MemoryStream tmpMemory = new MemoryStream(System.IO.File.ReadAllBytes(filePath)))
{
tmpMemory.CopyTo(entryStream);
};
}
}
zipArchiveMemoryStream.Seek(0, SeekOrigin.Begin);
result = zipArchiveMemoryStream.ToArray();
}
return File(result, "application/zip", zipFileName);
}
This is taken from a recent ASP.NET project of my own.

Find Encoding type of DeflateStream

I have a zip file with a csv file inside it. I am using following code to read the file:
using (ZipArchive zipArchive = ZipFile.OpenRead(filePath))
{
var zipArchiveEntry = zipArchive.GetEntry("File.csv");
var zipEntry = zipArchiveEntry.Open();
...
}
The zipEntry is of type System.IO.Compreesion.Deflatestream.
I tried using StreamReader.CurrentEncoding, but its giving wrong encoding value.
I am using this solution now,
this.GetFileEncoding(zipEntry)
but getting NotSupportedException at fileStrem.Length.
How do i find the right Encoding of the zipEntry (File.csv)?
Provided you can assure that the file is in fact a cvs file then just use a stream reader to get it contents from the entry's stream when opened
using (ZipArchive archive = ZipFile.OpenRead(filePath)) {
var entry = archive.GetEntry("File.csv");
if (entry != null) {
using (var reader = new StreamReader(entry.Open())) {
var csv = reader.ReadToEnd();
}
}
}

Invalid zip file after creating it with System.IO.Compression

I'm trying to create a zip file that contains one or more files.
I'm using the .NET framework 4.5 and more specifically System.IO.Compression namespace.
The objective is to allow a user to download a zip file through a ASP.NET MVC application.
The zip file is being generated and sent to the client but when I try to open it by doing double click on it I get the following error:
Windows cannot open the folder.
The compressed (zipped) folder ... is invalid.
Here's my code:
[HttpGet]
public FileResult Download()
{
var fileOne = CreateFile(VegieType.POTATO);
var fileTwo = CreateFile(VegieType.ONION);
var fileThree = CreateFile(VegieType.CARROT);
IEnumerable<FileContentResult> files = new List<FileContentResult>() { fileOne, fileTwo, fileThree };
var zip = CreateZip(files);
return zip;
}
private FileContentResult CreateFile(VegieType vType)
{
string fileName = string.Empty;
string fileContent = string.Empty;
switch (vType)
{
case VegieType.BATATA:
fileName = "batata.csv";
fileContent = "THIS,IS,A,POTATO";
break;
case VegieType.CEBOLA:
fileName = "cebola.csv";
fileContent = "THIS,IS,AN,ONION";
break;
case VegieType.CENOURA:
fileName = "cenoura.csv";
fileContent = "THIS,IS,A,CARROT";
break;
default:
break;
}
var fileBytes = Encoding.GetEncoding(1252).GetBytes(fileContent);
return File(fileBytes, MediaTypeNames.Application.Octet, fileName);
}
private FileResult CreateZip(IEnumerable<FileContentResult> files)
{
byte[] retVal = null;
if (files.Any())
{
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
{
foreach (var f in files)
{
var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
using (var entryStream = entry.Open())
{
entryStream.Write(f.FileContents, 0, f.FileContents.Length);
entryStream.Close();
}
}
zipStream.Position = 0;
retVal = zipStream.ToArray();
}
}
}
return File(retVal, MediaTypeNames.Application.Zip, "horta.zip");
}
Can anyone please shed some light on why is windows saying that my zip file is invalid when I double click on it.
A final consideration, I can open it using 7-Zip.
You need to get the MemoryStream buffer via ToArray after the ZipArchive object gets disposed. Otherwise you end up with corrupted archive.
And please note that I have changed the parameters of ZipArchive constructor to keep it open when adding entries.
There is some checksumming going on when the ZipArchive is beeing disposed so if you read the MemoryStream before, it is still incomplete.
private FileResult CreateZip(IEnumerable<FileContentResult> files)
{
byte[] retVal = null;
if (files.Any())
{
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
foreach (var f in files)
{
var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
using (BinaryWriter writer = new BinaryWriter(entry.Open()))
{
writer.Write(f.FileContents, 0, f.FileContents.Length);
writer.Close();
}
}
zipStream.Position = 0;
}
retVal = zipStream.ToArray();
}
}
return File(retVal, MediaTypeNames.Application.Zip, "horta.zip");
}
Just return the stream...
private ActionResult CreateZip(IEnumerable files)
{
if (files.Any())
{
MemoryStream zipStream = new MemoryStream();
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
{
foreach (var f in files)
{
var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
using (var entryStream = entry.Open())
{
entryStream.Write(f.FileContents, 0, f.FileContents.Length);
entryStream.Close();
}
}
}
zipStream.Position = 0;
return File(zipStream, MediaTypeNames.Application.Zip, "horta.zip");
}
return new EmptyResult();
}
Try changing
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
to
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
In this usage, the archive is forced to write to the stream when it is closed. However, if the leaveOpen argument of the constructor is set to false, it will close the underlying stream too.
When I added a wrong name for the entry as in the example
var fileToZip = "/abc.txt";
ZipArchiveEntry zipFileEntry = zipArchive.CreateEntry(fileToZip);
I got the same error. After correcting the file name, it is ok now.
I got the "The compressed (zipped) folder ... is invalid." error because my entries were named with a leading "/" in front of them. Some zip extractors had no problem with this but the Windows one does. I resolved it by removing the slash from the entry name (from "/file.txt" to "file.txt").

How to reset the stream of StreamReader to beginning?

I have referred this Return StreamReader to Beginning, but couldn't figure out this problem.
This is code to read stream of a particular file in zip file. Here there are two stream of files inside two different zip files. Now I need to compare the streams.
I am unable to set the stream of BaseFileReader stream to beginning of stream.
using (FileStream BaseZipToOpen = new FileStream(BaseArchive,FileMode.Open) , CurrentZipToOpen = new FileStream(CurrentArchive,FileMode.Open))
{
using (ZipArchive BaseZip = new ZipArchive(BaseZipToOpen, ZipArchiveMode.Read), CurrentZip = new ZipArchive(CurrentZipToOpen, ZipArchiveMode.Read))
{
ZipArchiveEntry BaseFile = BaseZip.GetEntry(requiredFile);
ZipArchiveEntry CurrentFile = CurrentZip.GetEntry(requiredFile);
using (StreamReader BaseFileReader = new StreamReader(BaseFile.Open()), CurrentFileReader = new StreamReader(CurrentFile.Open()))
{
string baseFileLine, currentFileLine;
while (!CurrentFileReader.EndOfStream)
{
currentFileLine = CurrentFileReader.ReadLine();
while (!BaseFileReader.EndOfStream)
{
baseFileLine = BaseFileReader.ReadLine();
if (!currentFileLine.Equals(baseFileLine))
{
difference = true;
}
else
{
difference = false;
break;
}
}
// how to reset BaseFileReader Stream to beginning?
BaseZipToOpen.Seek(0, SeekOrigin.Begin); //This is not working
}
}
}
}
You can use
FileStream stream = new FileStream();
stream.Position = 0;

How to save downloaded files in MemoryStream when using SSH.NET

I am using SSH.NET library to download files. I want to save the downloaded file as a file in memory, rather than a file on disk but it is not happening.
This is my code which works fine:
using (var sftp = new SftpClient(sFTPServer, sFTPPassword, sFTPPassword))
{
sftp.Connect();
sftp.DownloadFile("AFile.txt", System.IO.File.Create("AFile.txt"));
sftp.Disconnect();
}
and this is the code which doesn't work fine as it gives 0 bytes stream.
using (var sftp = new SftpClient(sFTPServer, sFTPPassword, sFTPPassword))
{
sftp.Connect();
System.IO.MemoryStream mem = new System.IO.MemoryStream();
System.IO.TextReader textReader = new System.IO.StreamReader(mem);
sftp.DownloadFile("file.txt", mem);
System.IO.TextReader textReader = new System.IO.StreamReader(mem);
string s = textReader.ReadToEnd(); // it is empty
sftp.Disconnect();
}
You can try the following code, which opens the file on the server and reads it back into a stream:
using (var sftp = new SftpClient(sFTPServer, sFTPUsername, sFTPPassword))
{
sftp.Connect();
// Load remote file into a stream
using (var remoteFileStream = sftp.OpenRead("file.txt"))
{
var textReader = new System.IO.StreamReader(remoteFileStream);
string s = textReader.ReadToEnd();
}
}
For simple text files, it's even easier:
var contents = sftp.ReadAllText(fileSpec);
I had a similar issue with the ScpClient, I needed to reset the stream position to the beginning after downloading the file.
using (var sftp = new SftpClient(sFTPServer, sFTPPassword, sFTPPassword))
{
sftp.Connect();
System.IO.MemoryStream mem = new System.IO.MemoryStream();
System.IO.TextReader textReader = new System.IO.StreamReader(mem);
sftp.DownloadFile("file.txt", mem);
// Reset stream to the beginning
mem.Seek(0, SeekOrigin.Begin);
System.IO.TextReader textReader = new System.IO.StreamReader(mem);
string s = textReader.ReadToEnd();
sftp.Disconnect();
}

Categories