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);
}
}
}
Here is the functionality I want to achieve
Write a JSON file.
Write a PDF file.
Create an archive for these two files
I am using the System.IO.Compression ZipArchive to achieve this. From the documentation, I have not found a good use case for this. The examples in documentation assume that the zip file exists.
What I want to do
Create zipArchive stream write JSON file and pdf file as entries in the zip file.
using (var stream = new FileStream(path, FileMode.Create))
{
using (var archive = new ZipArchive(stream, ZipArchiveMode.Create, true))
{
ZipArchiveEntry manifest = archive.CreateEntry(filenameManifest);
using (StreamWriter writerManifest = new StreamWriter(manifest.Open()))
{
writerManifest.WriteLine(JSONObject_String);
}
ZipArchiveEntry pdfFile = archive.CreateEntry(filenameManifest);
using (StreamWriter writerPDF = new StreamWriter(pdfFile.Open()))
{
writerPDF.WriteLine(pdf);
}
}
}
You don't close the stream, you open with 'manifest.Open()'. Then it might not have written everything to the zip.
Wrap it in another using, like this:
using (var stream = new FileStream(path, FileMode.Create))
{
using (var archive = new ZipArchive(stream, ZipArchiveMode.Create, true))
{
ZipArchiveEntry manifest = archive.CreateEntry(filenameManifest);
using (Stream st = manifest.Open())
{
using (StreamWriter writerManifest = new StreamWriter(st))
{
writerManifest.WriteLine(JSONObject_String);
}
}
ZipArchiveEntry pdfFile = archive.CreateEntry(filenameManifest);
using (Stream st = manifest.Open())
{
using (StreamWriter writerPDF = new StreamWriter(st))
{
writerPDF.WriteLine(pdf);
}
}
}
}
I want to rewrite text file using StreamWriter. but when StreamWriter uses stream (like following code), the text will append to file.
StreamWriter sw = new StreamWriter(fstream);
sw.Write(text);
sw.Close();
i must use Stream in code because of that file share limitation
FileMode.Create ll create a new file. If the file excists, it ll show exception. Use FileMode.Truncate.
string txt="your text";
using (FileStream fs = new FileStream(#"C:\Users\rajesh.kumar\Desktop\test123.txt", FileMode.Truncate))
{
using (StreamWriter writer = new StreamWriter(fs))
{
writer.Write("txt");
}
}
If you use file stream, set FileMode:
using (FileStream fs = new FileStream(fileName, FileMode.CreateNew))
{
using (StreamWriter writer = new StreamWriter(fs))
{
writer.Write(textToAdd);
}
}
I have a text file with the following text inside:
[username][0]
I have opened the file using StreamWriter and I want to change the 0 to a 1 using the StreamWriter.Write Method. How can I do this?
If you know the exact byte position of the character(s) you want to overwrite then you can do something like this:
using (var writer = new StreamWriter(filePath))
{
writer.BaseStream.Seek(bytePos, SeekOrigin.Begin);
writer.Write('1');
}
If you don't know the exact byte position then you could do something like this:
using (var file = new FileStream(filePath, FileMode.Open))
using (var reader = new StreamReader(file))
using (var writer = new StreamWriter(file))
{
var openBracketCount = 0;
// Keep reading characters until the second open bracket is found.
do
{
var ch = Convert.ToChar(reader.Read());
if (ch == '[')
{
openBracketCount++;
}
} while (openBracketCount < 2);
writer.Write('1');
}
I've just started using filestream, and although I made the code work,- I would really like to make it pretty as well :) I have no idea where to place the using statements so I can skip the stream.Close(), and how to use try catch finally. here is my code, not the prettiest thing, but it works. The double filestream is used to clear the file.
Edit: sorry for posting that code snippet blush that was pretty bad :P I've posted my second try :)
internal static void SaveFileAsTxt()
{
FileStream streamer = new FileStream("Shipping2.txt", FileMode.Append, FileAccess.Write, FileShare.Write);
streamer.Close();
FileStream f = File.Open("Shipping2.txt", FileMode.Create);
f.Close();
StreamWriter writer = new StreamWriter("Shipping2.txt", true, Encoding.ASCII);
foreach (var shipment in _shipments)
{
string write = (shipment.Distance + ","+ shipment.Distance).ToString();
writer.WriteLine(write);
};
writer.Close();
}
//--------new code--------
internal static void SaveFileAsTxt()
{
if (File.Exists("Shipping2.txt"))
{
File.Delete("Shipping2.txt");
}
using (StreamWriter writer = new StreamWriter("Shipping2.txt", true, Encoding.ASCII))
{
foreach (var shipment in _shipments)
{
string write = (shipment.Duration + ","+ shipment.Distance).ToString();
writer.WriteLine(write);
}
}
}
You don't need to open the file more than once - and you're actually opening it three times at the moment. This should be fine; File.CreateText will truncate the file if it already exists, and create it otherwise:
// Are you *sure* you want to use ASCII? UTF-8 might be a better bet...
using (TextWriter writer = File.CreateText("Shipping2.txt", Encoding.ASCII))
{
foreach (var shipment in _shipments)
{
// Removed redundant ToString call, and elided local variable.
// Consider using a format string instead:
// writer.WriteLine("{0},{1}", shipment.Distance, shipment.Distance);
writer.WriteLine(shipment.Distance + "," + shipment.Distance);
}
// Removed empty statement (trailing semi-colon)
}
Now, you say you want to use try/catch/finally - but why? If you fail to write to the file, do you definitely want to "handle" the exception in this method, rather than letting it bubble up to the caller?
Firstly, I dont understand the use for so many streams but you can use using in a number of places:
internal static void SaveFileAsTxt()
{
using(var streamer = new FileStream("Shipping2.txt", FileMode.Append, FileAccess.Write, FileShare.Write))
{
}
using(var f = File.Open("Shipping2.txt", FileMode.Create))
{
}
using(var writer = new StreamWriter("Shipping2.txt", true, Encoding.ASCII))
{
foreach (var shipment in _shipments)
{
string write = (shipment.Distance + ","+ shipment.Distance).ToString();
writer.WriteLine(write);
};
}
}
You can skip two first FileStreams and just use StreamWriter, it will create a file for you :
// Single using
using (StreamWriter writer = new StreamWriter(
"Shipping2.txt",
true, // !!!
Encoding.ASCII))
{
foreach (var shipment in _shipments)
{
string write = (shipment.Distance + "," + shipment.Distance)
.ToString();
writer.WriteLine(write);
}
}
MSDN:
public StreamWriter(
string path,
bool append,
Encoding encoding
)
append
Type: System.Boolean Determines whether data is to be appended to the
file. If the file exists and append is false, the file is overwritten.
If the file exists and append is true, the data is appended to the
file. Otherwise, a new file is created.
EDIT: Regarding an updated question (second part)
You do not need to delete file manually, just specify append = false in constructor of StreamWriter and it will overwrite a file:
If the file exists and append is false, the file is overwritten
Right now, the code you have is equivalent to this:
internal static void SaveFileAsTxt() {
using (FileStream streamer = new FileStream(
"Shipping2.txt", FileMode.Append, FileAccess.Write, FileShare.Write
);) {}
using(FileStream f = File.Open("Shipping2.txt", FileMode.Create)) {}
using(StreamWriter writer =
new StreamWriter("Shipping2.txt", true, Encoding.ASCII)) {
foreach (var shipment in _shipments) {
string write = (shipment.Distance + "," + shipment.Distance).ToString();
writer.WriteLine(write);
}
}
}
But I don't know why you opened the first file.
In respect to the second, I believe you tried to create the file using this method,
but there are other ways to do that as well.
// Single using
using (StreamWriter writer = new StreamWriter(
"Shipping2.txt",
true, // !!!
Encoding.ASCII))
{
foreach (var shipment in _shipments)
{
string write = (shipment.Distance + "," + shipment.Distance)
.ToString();
writer.WriteLine(write);
}
}
Just to be different, I'll use LINQ :-)
File.WriteAllLines(
"Shipping2.txt",
_shipments.Select(
p => string.Format(
"{0},{1}",
shipment.Distance,
shipment.Distance)
),
Encoding.ASCII);
This version requires .NET 4.0. File.WriteAllLines will create the file, write all the lines and close the file. _shipments.Select(p => string.Format("{0},{1}", shipment.Distance, shipment.Distance)) will return, for each element of _shipments, a string containing shipment.Distance + "," + shipment.Distance (formatted using string.Format)
`