Does stream reused allowed in C#? - c#

Here is my code.
using (FileStream zipToOpen = new FileStream(#"D:\test\1.txt", FileMode.Open))
{
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
{
ZipArchiveEntry readmeEntry = archive.CreateEntry("Readme.txt");
using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
{
writer.WriteLine("Information about this package.");
writer.WriteLine("========================");
}
}
// I reused this stream again and below code just a sample.
// It would get exception
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
{
//System.ArgumentException: 'Update mode requires a stream with read, write, and seek capabilities.'
}
}
Is this exception caused by 'stream cannot be resued in C#' ?
If the answer is yes, any office documents can let me take a reference ?
Thanks in advance, it's appreciate if your guys can provide some help.

By default, ZipArchive assumes lifetime control of the Stream it is handed, and disposes it when it is disposed. To avoid this, set leaveOpen to true in the constructor overloads. However: you will also probably need to rewind the stream between usages, so you'll need zipToOpen.Position = 0; between the two using blocks. Anecdotally, it does seem unlikely that 1.txt is a zip file, but... I guess it could be!

Related

ZipArchive created with C# does not contain any entries

I am attempting to create a zipfile in ASP.NET MVC a single PDF file within it. However, using the code below, an empty zipfile is created. Can someone please advise what I am doing incorrectly?
public FileResult DownloadZipfile(string html)
{
MemoryStream memoryStream = new MemoryStream();
ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
byte[] rawDownload = PDFConverterUtils.PdfSharpConvert(html);
ZipArchiveEntry entry = archive.CreateEntry("MyPDF.pdf");
using (Stream entryStream = entry.Open())
using (StreamWriter streamWriter = new StreamWriter(entryStream))
{
streamWriter.BaseStream.Write(rawDownload, 0, rawDownload.Length);
}
return new FileStreamResult(memoryStream, System.Net.Mime.MediaTypeNames.Application.Zip) { FileDownloadName = "test.zip" };
}
When using a ZipArchive with a MemoryStream, I would suggest resetting the position of the stream after writing to it so that the content of the stream can be read by the response.
public FileResult DownloadZipfile(string html) {
byte[] rawDownload = PDFConverterUtils.PdfSharpConvert(html);
MemoryStream memoryStream = new MemoryStream();
using(ZipArchive archive = new ZipArchive(
stream: memoryStream,
mode: ZipArchiveMode.Create,
leaveOpen: true //To leave the memory stream open after disposal
)){
ZipArchiveEntry entry = archive.CreateEntry("MyPDF.pdf");
using (Stream entryStream = entry.Open()) {
entryStream.Write(rawDownload, 0, rawDownload.Length);
}
}
memoryStream.Position = 0;//reset memory stream position for read
return new FileStreamResult(memoryStream, System.Net.Mime.MediaTypeNames.Application.Zip) {
FileDownloadName = "test.zip"
};
}
As suggested in another answer you should dispose of the archive to force it to write its content to its underlying memory stream, but take note of the following
ZipArchive.Dispose()
Unless you construct the object by using the ZipArchive(Stream, ZipArchiveMode, Boolean) constructor overload and set its leaveOpen parameter to true, all underlying streams are closed and no longer available for subsequent write operations.
When you are finished using this instance of ZipArchive, call Dispose() to release all resources used by this instance. You should eliminate further references to this ZipArchive instance so that the garbage collector can reclaim the memory of the instance instead of keeping it alive for finalization.
Because you want to make use of the memory stream after writing to it, you need to make sure that it remains open, and that the position of the stream is reset to the beginning so that the content of the stream can be read from the start.

create zip from byte[] and return to browser

I want to create a zip-file and return it to the browser so that it downloads the zip to the downloads-folder.
var images = imageRepository.GetAll(allCountryId);
using (FileStream f2 = new FileStream("SudaAmerica", FileMode.Create))
using (GZipStream gz = new GZipStream(f2, CompressionMode.Compress, false))
{
foreach (var image in images)
{
gz.Write(image.ImageData, 0, image.ImageData.Length);
}
return base.File(gz, "application/zip", "SudaAmerica");
}
i have tried the above but then i get an error saying the stream is disposed.
Is this possible or should i use another library then gzipstream?
The problem here is exactly what it says: you are handing it something based on gz, but gz gets disposed the moment you leave the using.
One option would be to wait until outside the using block, then tell it to use the filename of the thing you just wrote ("SudaAmerica"). However, IMO you shouldn't actually be writing a file here at all. If you use a MemoryStream instead, you can use .ToArray() to get a byte[] of the contents, which you can use in the File method. This requires no IO access, which is a win in about 20 different ways. Well, maybe 3 ways. But...
var images = imageRepository.GetAll(allCountryId);
using (MemoryStream ms = new MemoryStream())
{
using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress, false))
{
foreach (var image in images)
{
gz.Write(image.ImageData, 0, image.ImageData.Length);
}
}
return base.File(ms.ToArray(), "application/zip", "SudaAmerica");
}
Note that a gzip stream is not the same as a .zip archive, so I very much doubt this will have the result you want. Zip archive creation is available elsewhere in the .NET framework, but it is not via GZipStream.
You probably want ZipArchive

Does CopyTo store the whole thing in memory?

I have the following code snippet, which is designed to add files to a .zip file, while at the same time calculating their sha1 checksum.
However, it's running out of memory on large files.
Which part of it is causing the whole file to be in memory? Surely this should all be just streamed?
using (ZipArchive archive = ZipFile.Open(buildFile, ZipArchiveMode.Update))
{
foreach (var fileName in nameList)
{
ZipArchiveEntry entry = archive.CreateEntry(source.filename);
using (Stream zipData = entry.Open())
using (SHA1Managed shaForFile = new SHA1Managed())
using (Stream sourceFileStream = File.OpenRead(fileName))
using (Stream sourceData = new CryptoStream(sourceFileStream, shaForFile, CryptoStreamMode.Read))
{
sourceData.CopyTo(zipData);
print fileName + ':' + shaForFile.Hash;
}
}
}
(Copied from a comment - as this answers the question)
The problem is ZipArchiveMode.Update, that can require significant alterations to the file on disk. It can only ever directly stream to disk when you use ZipArchiveMode.Create

Modify File Stream in memory

I am reading a file using StreamReader fileReader = File.OpenText(filePath). I would like to modify one line in the file in memory and push the modified stream to another method.
What I would like to avoid is reading the whole file into a string and modifying the string (doesn't scale). I would also like to avoid modifying the actual file.
Is there a straightforward way of doing this?
There is no built-in way to do that in .Net framework.
Stream and StreamReader/StreamWriter classes are designed to be chained if necessary (like GZipStream wraps stream to compress it). So you can create wrapper StreamReader and update data as you need for every operation after calling wrapped reader.
You can open two stream -one for read, one for write- at the same time. I tested simple code that works, but not sure that's what you want:
// "2.bar\r\n" will be replaced by "!!!!!\r\n"
File.WriteAllText("test.txt",
#"1.foo
2.bar
3.fake");
// open inputStream for StreamReader, and open outputStream for StreamWriter
using (var inputStream = File.Open("test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var reader = new StreamReader(inputStream))
using (var outputStream = File.Open("test.txt", FileMode.Open, FileAccess.Write, FileShare.Read))
using (var writer = new StreamWriter(outputStream))
{
var position = 0L; // track the reading position
var newLineLength = Environment.NewLine.Length;
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
// your particular conditions here.
if (line.StartsWith("2."))
{
// seek line start position
outputStream.Seek(position, SeekOrigin.Begin);
// replace by something,
// but the length should be equal to original in this case.
writer.WriteLine(new String('!', line.Length));
}
position += line.Length + newLineLength;
}
}
/* as a result, test.txt will be:
1.foo
!!!!!
3.fake
*/
As you can see, both streams can be accessed by StreamReader and StreamWriter at the same time. And you can also manipulate both read/write position as well.

GZipStream only decompresses first line

My GZipStream will only decompress the first line of the file. Extracting the contents via 7-zip works as expected and gives me the entire file contents. It also extracts as expected using gunzip on cygwin and linux, so I expect this is O/S specific (Windows 7).
I'm not certain how to go about troubleshooting this, so any tips on that would help me a great deal. It sounds very similar to this, but using SharpZLib results in the same thing.
Here's what I'm doing:
var inputFile = String.Format(#"{0}\{1}", inputDir, fileName);
var outputFile = String.Format(#"{0}\{1}.gz", inputDir, fileName);
var dcmpFile = String.Format(#"{0}\{1}", outputDir, fileName);
using (var input = File.OpenRead(inputFile))
using (var fileOutput = File.Open(outputFile, FileMode.Append))
using (GZipStream gzOutput = new GZipStream(fileOutput, CompressionMode.Compress, true))
{
input.CopyTo(gzOutput);
}
// Now, decompress
using (FileStream of = new FileStream(outputFile, FileMode.Open, FileAccess.Read))
using (GZipStream ogz = new GZipStream(of, CompressionMode.Decompress, false))
using (FileStream wf = new FileStream(dcmpFile, FileMode.Append, FileAccess.Write))
{
ogz.CopyTo(wf);
}
Your output file only contains a single line (gzipped) - but it contains all of the text data other than the line breaks.
You're repeatedly calling ReadLine() which returns a line of text without the line break and converting that text to bytes. So if you had an input file which had:
abc
def
ghi
You'd end up with an output file which was the compressed version of
abcdefghi
If you don't want that behaviour, why even go through a StreamReader in the first place? Just copy from the input FileStream straight to the GZipStream a block at a time, or use Stream.CopyTo if you're using .NET 4:
// Note how much simpler the code is using File.*
using (var input = File.OpenRead(inputFile))
using (var fileOutput = File.Open(outputFile, FileMode.Append))
using (GZipStream gzOutput = new GZipStream(os, CompressionMode.Compress, true))
{
input.CopyTo(gzOutput);
}
Also note that appending to a compressed file is rarely a good idea, unless you've got some sort of special handling for multiple "chunks" within a single file.

Categories

Resources