Download zip file from the server and parsing it - c#

I am trying to download a zipped file from the server and trying to show the content of each files in zipped folder to the view.
I wrote a separate code where the file is on my laptop and I ran across each file and dislpayed the content such as
static void Main(string[] args)
{
string filePath = "C:\\ACL Data\\New folder\\files.zip";
var zip= new ZipInputStream(File.OpenRead(filePath));
var filestream=new FileStream(filePath,FileMode.Open,FileAccess.Read);
ZipFile zipfile = new ZipFile(filestream);
ZipEntry item;
while ((item = zip.GetNextEntry()) != null)
{
Console.WriteLine(item.Name);
using (StreamReader s = new StreamReader(zipfile.GetInputStream(item)))
{
Console.WriteLine(s.ReadToEnd());
}
}
Console.Read();
}
I am using sharplibzip library to implement this
This is the case when the zip file is located locally in the system. My next task scenario is what if the zipped file is located on the server. I am figuring out the way to implement it, below is the code what I assume should work
static void Main(string[] args)
{
string url = "https://test/code/304fd9c6-7e53-42a2-845a-624608bfd2ce.zip";
WebRequest webRequest = WebRequest.Create(url);
webRequest.Method = "GET";
WebResponse webResponse = webRequest.GetResponse();
var zip = new ZipInputStream(webResponse.GetResponseStream());
ZipEntry item1;
//var zip= new ZipInputStream(File.OpenRead(filePath));
var filestream = new FileStream(filepath, FileMode.Open, FileAccess.Read);
ZipFile zipfile = new ZipFile(filestream);
ZipEntry item;
while ((item = zip.GetNextEntry()) != null)
{
Console.WriteLine(item.Name);
using (StreamReader s = new StreamReader(zipfile.GetInputStream(item)))
{
Console.WriteLine(s.ReadToEnd());
}
}
Console.Read();
}
I am stuck at this part: var filestream = new FileStream(filepath, FileMode.Open, FileAccess.Read);
This expect the first parameter to be path of the zip file. Since in the new scenario zip file is located remotely on the server. What should be the parameter in this case?

Your original code opens the stream twice on the following rows, which I think is causing some confusion:
var zip= new ZipInputStream(File.OpenRead(filePath));
var filestream=new FileStream(filePath,FileMode.Open,FileAccess.Read);
There is an overload to the ZipFile constructor that takes "any" Stream rather than specifically a FileStream, which you - unsurprisingly - can only create for files.
However, you cannot use the stream returned by GetResponseStream directly, because it's CanSeek property is false. This is because it's a NetworkStream, which can only be read once from beginning to end. SharpZipLib needs random access to read the file contents.
Depending on the size of the ZIP file, loading it in memory may be an option. If you expect large files, writing it to a temporary file may be better.
This should do the trick, without using both ZipInputStream and ZipFile, by enumerating through ZipFile instead:
string url = "https://test/code/304fd9c6-7e53-42a2-845a-624608bfd2ce.zip";
WebRequest webRequest = WebRequest.Create(url);
webRequest.Method = "GET";
WebResponse webResponse = webRequest.GetResponse();
using (var responseStream = webResponse.GetResponseStream())
using (var ms = new MemoryStream())
{
// Copy entire file into memory. Use a file if you expect a lot of data
responseStream.CopyTo(ms);
var zipFile = new ZipFile(ms);
foreach (ZipEntry item in zipFile)
{
Console.WriteLine(item.Name);
using (var s = new StreamReader(zipFile.GetInputStream(item)))
{
Console.WriteLine(s.ReadToEnd());
}
}
}
Console.Read();
PS: starting .NET 4.5, there is support for ZIP files built in. See the ZipArchive class.

Related

FileResult content-length mismatch

Hi im using the code in this blogpost :
https://blog.stephencleary.com/2016/11/streaming-zip-on-aspnet-core.html
In order to stream a zip file with .Net core. I made it work but since i did not add the content-length header in the response when i donwload the zip file, it won't show the download progress in chrome. Since i know in advance the zip file size I can actually set the content-length header, with the SetHeadersAndLog method
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.internal.fileresultexecutorbase.setheadersandlog?view=aspnetcore-2.0
but when i do so I have the following error :
System.InvalidOperationException: Response Content-Length mismatch: too many bytes written (144144633 of 144144627).
Any idea why the response is not the same length as the zip file ?
Here's the code to serve the file:
this._httpContext.Response.ContentType = "application/octet-stream";
this._httpContext.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
this._httpContext.Response.ContentLength = estimatedFileSize;
FileCallbackResult result = new FileCallbackResult(new MediaTypeHeaderValue("application/octet-stream"), estimatedFileSize, async (outputStream, _) =>
{
using (ZipArchive zip = new ZipArchive(outputStream, ZipArchiveMode.Create, false))
{
foreach (string filepath in Directory.EnumerateFiles(existingDirectory.FullName, "*.*", SearchOption.AllDirectories))
{
string relativepath = filepath.Replace(existingDirectory.FullName + "\\", string.Empty);
ZipArchiveEntry zipEntry = zip.CreateEntry(relativepath, CompressionLevel.Fastest);
using (Stream zipstream = zipEntry.Open())
{
using (Stream stream = new FileStream(filepath, FileMode.Open))
{
await stream.CopyToAsync(zipstream);
}
}
}
}
})
{
FileDownloadName = $"{package.FileName}.zip",
};
You need to seek the stream back to the beginning.

stream.ReadTimeout' threw an exception of type 'System.InvalidOperationException'

I am trying to download a zip file from SFTP and unzip in the memory to process the file
I am using SSH.Net to download the file.
private static void processfilesfromftp(List<TSOracleMicrosDownLoadSetUp> list)
{
SftpClient sftp = HelperFunctions.GetClientConnection();
if(sftp.IsConnected)
{
var files = sftp.ListDirectory("/");
ZipFile zips = new ZipFile();
string path = string.Empty;
foreach(var file in files)
{
Stream unzippedEntryStream = new MemoryStream();
path = string.Format("/{0}", file.Name);
//byte[] arr = sftp.ReadAllBytes(file.FullName);
var stream = new BufferedStream(sftp.OpenRead(file.FullName));
//System.IO.TextReader textReader = new System.IO.StreamReader(stream);
//System.IO.MemoryStream mStream = new MemoryStream();
using (ZipFile zip = ZipFile.Read(stream))
{
ZipEntry e = zip[0];
e.Extract(unzippedEntryStream);
System.IO.TextReader textReader = new System.IO.StreamReader(unzippedEntryStream);
string data = textReader.ReadToEnd();
}
}
}
}
memorystream throw error System.InvalidOperationException exception at
var stream = new BufferedStream(sftp.OpenRead(file.FullName));
Update
It is not throwing any error, but the final output of the unzip file is empty.
Using Framework 4.5.2 and Visual studio 2017
This is more a SSH.Net question and not specific Acumatica.
It seems the problem is related to the SSH connection.
To change the timeout you can use SshClient.ConnectionInfo.Timeout. But you need to catch the exception and handle it gracefully.
Here is a post with a similar issue.
BTW, you could use the included Acumatica library to read the zip file.
I think you are not writing the file from FTP to the memory stream so it's empty.
Try using the DownloadFile method from SSH.Net to write file content in the stream.
Reference:
https://stackoverflow.com/a/46907346/7376238
SftpClient _sftpClient;
_sftpClient = new SftpClient("sftp.server.domain", "MyLoginHere", "MyPasswordHere");
Stream fileBody = new MemoryStream();
_sftpClient.DownloadFile(ftpFile.FullName, fileBody);
fileBody.Position = 0;

Invalid HTTP response when sending a zip file from a MemoryStream

I am attempt to create a zip file in memory, from multiple other zip files read from file streams. It appears that it is able to read the files correctly, and create a zip file; however, when the response is being created all content headers get placed into invalidHeaders. This causes the download to never occur, and instead a bad response page is loaded.
using (var memoryStream = new MemoryStream())
{
// Gather all zips into a single zip file
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach(var zipFile in zipFiles)
{
archive.CreateEntryFromFile(zipFile.ZipFilePath, Path.GetFileName(zipFile.ZipFilePath));
}
}
// Now we have our memory stream with our zip
HttpResponseMessage message = new HttpResponseMessage(HttpStatusCode.OK);
message.Content = new StreamContent(memoryStream);
message.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
message.Content.Headers.ContentDisposition.FileName = "AllZIPFiles.zip";
message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
message.Content.Headers.ContentLength = memoryStream.Length;
// Content headers placed into invalidHeaders?
return ResponseMessage(message);
}
After the ZipArchive does its work, the position of the stream will be at the end of the stream.
Before sending such stream as a response, make sure that you set the stream position to 0 like this:
memoryStream.Position = 0;

C# FTP The given path's format is not supported

This is my code
I have an FTP with many zip files. and each zip file has a XML with the same name.
I want to parse these xml files.
What i did is this:
get a list of all zip files in the FTP and save the names in this variable directories.
Now I want to open each zip file, which its name is in the directories list. I did this.
foreach (string fileNameInFTP in directories)
{
}
Now to read the content of that zip file, I tried this:.
string fileName = FTPAddress + fileNameInFTP;
using (var file = File.OpenRead(fileName))
using (var zip = new ZipArchive(file, ZipArchiveMode.Read))
{
foreach (var entry in zip.Entries)
{
using (var stream = entry.Open())
{
// do whatever we want with stream
// ...
}
}
}
I got this exception The given path's format is not supported. on this line:
using (var file = File.OpenRead("ftp://" +FTPAddress +"/" + fileNameInFTP)) could u help please
You should use something like this instead of trying to use File.OpenRead for remote FTP file download.
http://msdn.microsoft.com/en-us/library/ms229711%28v=vs.110%29.aspx
// Get the object used to communicate with the server.
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://www.contoso.com/test.htm");
request.Method = WebRequestMethods.Ftp.DownloadFile;
// This example assumes the FTP site uses anonymous logon.
request.Credentials = new NetworkCredential ("anonymous","janeDoe#contoso.com");
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
using (var zip = new ZipArchive(responseStream , ZipArchiveMode.Read))
{
//Loops through each file in the zip that has the ".xml" extension
foreach (var entry in zip.Entries.Where(x=> (Path.GetExtension(x.Name) ?? "").ToLower() ==".xml"))
{
using (var stream = entry.Open())
{
//Load xml file and do whatever you like with it.
var xmlDocument = XDocument.Load(stream);
}
}
}
Console.WriteLine("Download Complete, status {0}", response.StatusDescription);
response.Close();
you can't use File IO to open FTP stream, here is a sample of how we can open FTP using WebRequest in .NET:
private static void Main(string[] args)
{
var ftp = WebRequest.Create(#"ftp://ftp.microsoft.com/softlib/MSLFILES/aspwebwiz2k.zip");
//ftp.Credentials=new NetworkCredential("anonymous","anonymous");
var response=ftp.GetResponse();
var stream=response.GetResponseStream();
var ms = ToMemoryStream(stream);
var archive = new ZipArchive(ms, ZipArchiveMode.Read);
var entry=archive.GetEntry("file name here");
var doc=XDocument.Load(entry.Open());
}
public static MemoryStream ToMemoryStream( Stream stream)
{
var memoryStream = new MemoryStream();
var buffer = new byte[4096];
while (true)
{
var readCount = stream.Read(buffer, 0, buffer.Length);
if (readCount == 0)
break;
memoryStream.Write(buffer, 0, readCount);
}
memoryStream.Position = 0;
return memoryStream;
}

Extracting zip file in memory failing with C# DotNetZip

I'm trying to download and extract a zip file in C#, specifically DotNetZip.
When I run this code...
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(reportUrl);
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
Stream stream = response.GetResponseStream();
MemoryStream ms = new MemoryStream();
stream.CopyTo(ms);
ms.Seek(0, 0);
ZipInputStream zip = new ZipInputStream(ms);
zip.Seek(0, 0);
ZipEntry e = zip.GetNextEntry();
string s = e.FileName;
MemoryStream ms2 = new MemoryStream();
e.Extract(ms2);
After the Extract method executes, I get...
$exception {"Object reference not set to an instance of an object."} System.Exception {System.NullReferenceException}
Any thoughts? Thanks!
It's difficult to say why your code doesn't work. I would start by simplifying it and ensuring that I am properly disposing all disposable resources such as streams:
class Program
{
static void Main()
{
var url = "http://downloads.sourceforge.net/project/junit/junit/3.8.1/junit3.8.1.zip";
using (var client = new WebClient())
using (var zip = ZipFile.Read(client.DownloadData(url)))
{
foreach (var entry in zip)
{
entry.Extract(".");
}
}
}
}
Make sure you checkout the documentation for many useful examples of using the DotNetZip library.

Categories