I have been developing a web application with asp.net and I have smoe question about SharZipLib. I have a file called Template.odt (from Open Office) and this file is a compacted file (like docx) and we have some other files inside it (manifiest, xml, images etc). I need to open this file change a file called content.xml and styles.xml and save in another .odt file and give to my client. But I'm not sure if we can use temporary files, so I was thinking how to do this using MemoryStream.
Look what I got:
protected byte[ GetReport() {
Stream inputStream = File.OpenRead(Server.MapPath("~/Odt/Template.odt"));
var zipInputStream = new ZipInputStream(inputStream);
var outputStream = new MemoryStream();
var zipOutputStream = new ZipOutputStream(outputStream);
ZipEntry entry = zipInputStream.GetNextEntry();
while (entry != null) {
if (entry.Name == "content.xml")
// how change the content ?
else if (entry.Name == "styles.xml")
// how change the content ?
// how to add it or create folders in the output ?
zipOutputStream.Write( ??? );
entry = zipInputStream.GetNextEntry();
}
zipOutputStream.Flush();
return outputStream.ToArray();
}
I'm not sure if it's right but I think it's on the way.
I try to take ExtraData from ZipEntry instance but I got it null, is it normal ?
Can someone help me?
Thank you
An example of how you can update ZIP files in memory can be found here:
http://wiki.sharpdevelop.net/SharpZipLib_Updating.ashx#Updating_a_zip_file_in_memory_1
In your case, you probably have to load content.xml into a XmlDocument or XDocument to modify it - but that depends on what you are trying to change exactly.
As a sidemark: when using streams, make sure you are disposing of them. The easiest way is to wrap the operation in using statement:
using(var inputStream = File.OpenRead(Server.MapPath("~/Odt/Template.odt")))
{
// ...
}
More information on that: http://www.codeproject.com/Articles/6564/Understanding-the-using-statement-in-C
Related
I've work with large XML Files (~1000000 lines, 34mb) that are stored in a ZIP archive. The XML file is used at runtime to store and load app settings and measurements. The gets loadeted with this function:
public static void LoadFile(string path, string name)
{
using (var file = File.OpenRead(path))
{
using (var zip = new ZipArchive(file, ZipArchiveMode.Read))
{
var foundConfigurationFile = zip.Entries.First(x => x.FullName == ConfigurationFileName);
using (var stream = new StreamReader(foundConfigurationFile.Open()))
{
var xmlSerializer = new XmlSerializer(typeof(ProjectConfiguration));
var newObject = xmlSerializer.Deserialize(stream);
CurrentConfiguration = null;
CurrentConfiguration = newObject as ProjectConfiguration;
AddRecentFiles(name, path);
}
}
}
}
This works for most of the time.
However, some files don't get read to the end and i get an error that the file contains non valid XML. I used
foundConfigurationFile.ExtractToFile();
and fount that the readed file stops at line ~800000. But this only happens inside this code. When i open the file via editor everything is there.
It looks like the zip doesnt get loaded correctly, or for that matter, completly.
Am i running in some limitations? Or is there an error in my code i don't find?
The file is saved via:
using (var file = File.OpenWrite(Path.Combine(dirInfo.ToString(), fileName.ToString()) + ".pwe"))
{
var zip = new ZipArchive(file, ZipArchiveMode.Create);
var configurationEntry = zip.CreateEntry(ConfigurationFileName, CompressionLevel.Optimal);
var stream = configurationEntry.Open();
var xmlSerializer = new XmlSerializer(typeof(ProjectConfiguration));
xmlSerializer.Serialize(stream, CurrentConfiguration);
stream.Close();
zip.Dispose();
}
Update:
The problem was the File.OpenWrite() method.
If you try to override a file with this method it will result in a mix between the old file and the new file, if the new file is shorter than the old file.
File.OpenWrite() doenst truncate the old file first as stated in the docs
In order to do it correctly it was neccesary to use the File.Create() method. Because this method truncates the old file first.
I'm trying to open an archive Xml file (inside a zip file but not extracting it to a physical directory) in an in-memory stream then making changes to it and saving it. But archive xml file doesn't get overwritten rather it gets two copies of Xml data. One copy is the original copy of Xml data and the other one is changed/modified/edited copy of Xml data in the same archive file.
Here is my code, please help me overwrite the existing Xml data with the changes made rather than having 2 copies of Xml data in the same archive xml file.
static void Main(string[] args)
{
string rootFolder = #"C:\Temp\MvcApplication5\MvcApplication5\Package1";
string archiveName = "MvcApplication5.zip";
string folderFullPath = Path.GetFullPath(rootFolder);
string archivePath = Path.Combine(folderFullPath, archiveName);
string fileName = "archive.xml";
using (ZipArchive zip = ZipFile.Open(archivePath, ZipArchiveMode.Update))
{
var archiveFile = zip.GetEntry(fileName);
if (archiveFile == null)
{
throw new ArgumentException(fileName, "not found in Zip");
}
if (archiveFile != null)
{
using (Stream stream = archiveFile.Open())
{
XDocument doc = XDocument.Load(stream);
IEnumerable<XElement> xElemAgent = doc.Descendants("application");
foreach(var node in xElemAgent)
{
if(node.Attribute("applicationPool").Value!=null)
{
node.Attribute("applicationPool").Value = "MyPool";
}
}
doc.Save(stream);
}
Console.WriteLine("Document saved");
}
}
}
You are first reading the XML data from the stream and then writing to the same stream, which is pointing to the end of the file. To illustrate, let's say the old file contains ABCD and we want to replace this with 123.
The current approach would result in ABCD123, since the stream is pointing to the last char in ABCD.
If you reset the stream to position 0 (stream.Seek(0) before writing the changed file, the file would contain 123D, because it wouldn't reduce the file length.
The solution is to delete your old ZipArchiveEntry and create a new one.
I came across this same issue just now, and I fixed it by adding this first line:
stream.SetLength(0);
xmlDoc.Save(stream);
edit: I see you came across the same solution as you mentioned in the comments of the previous answer. You can add an answer to your own question. It would have helped someone like me :]
I've been having a bit of a problem lately. I've been trying to extract one zip file into a memory stream and then from that stream, use the updateEntry() method to add it to the destination zip file.
The problem is, when the file in the stream is being put into the destination zip, it works if the file is not already in the zip. If there is a file with the same name, it does not overwrite correctly. It says on the dotnetzip docs that this method will overwrite files that are present in the zip with the same name but it does not seem to work. It will write correctly but when I go to check the zip, the files that are supposed to be overwritten have a compressed byte size of 0 meaning something went wrong.
I'm attaching my code below to show you what I'm doing:
ZipFile zipnew = new ZipFile(forgeFile);
ZipFile zipold = new ZipFile(zFile);
using(zipnew) {
foreach(ZipEntry zenew in zipnew) {
percent = (current / zipnew.Count) * 100;
string flna = zenew.FileName;
var fstream = new MemoryStream();
zenew.Extract(fstream);
fstream.Seek(0, SeekOrigin.Begin);
using(zipold) {
var zn = zipold.UpdateEntry(flna, fstream);
zipold.Save();
fstream.Dispose();
}
current++;
}
zipnew.Dispose();
}
Although it might be a bit slow, I found a solution by manually deleting and adding in the file. I'll leave the code here in case anyone else comes across this problem.
ZipFile zipnew = new ZipFile(forgeFile);
ZipFile zipold = new ZipFile(zFile);
using(zipnew) {
// Loop through each entry in the zip file
foreach(ZipEntry zenew in zipnew) {
string flna = zenew.FileName;
// Create a new memory stream for extracted files
var ms = new MemoryStream();
// Extract entry into the memory stream
zenew.Extract(ms);
ms.Seek(0, SeekOrigin.Begin); // Rewind the memory stream
using(zipold) {
// Remove existing entry first
try {
zipold.RemoveEntry(flna);
zipold.Save();
}
catch (System.Exception ex) {} // Ignore if there is nothing found
// Add in the new entry
var zn = zipold.AddEntry(flna, ms);
zipold.Save(); // Save the zip file with the newly added file
ms.Dispose(); // Dispose of the stream so resources are released
}
}
zipnew.Dispose(); // Close the zip file
}
Given a stream object which contains an xlsx file, I want to save it as a temporary file and delete it when not using the file anymore.
I thought of creating a class that implementing IDisposable and using it with the using code block in order to delete the temp file at the end.
Any idea of how to save the stream to a temp file and delete it on the end of use?
Thanks
You could use the TempFileCollection class:
using (var tempFiles = new TempFileCollection())
{
string file = tempFiles.AddExtension("xlsx");
// do something with the file here
}
What's nice about this is that even if an exception is thrown the temporary file is guaranteed to be removed thanks to the using block. By default this will generate the file into the temporary folder configured on the system but you could also specify a custom folder when invoking the TempFileCollection constructor.
You can get a temporary file name with Path.GetTempFileName(), create a FileStream to write to it and use Stream.CopyTo to copy all data from your input stream into the text file:
var stream = /* your stream */
var fileName = Path.GetTempFileName();
try
{
using (FileStream fs = File.OpenWrite(fileName))
{
stream.CopyTo(fs);
}
// Do whatever you want with the file here
}
finally
{
File.Delete(fileName);
}
Another approach here would be:
string fileName = "file.xslx";
int bufferSize = 4096;
var fileStream = System.IO.File.Create(fileName, bufferSize, System.IO.FileOptions.DeleteOnClose)
// now use that fileStream to save the xslx stream
This way the file will get removed after closing.
Edit:
If you don't need the stream to live too long (eg: only a single write operation or a single loop to write...), you can, as suggested, wrap this stream into a using block. With that you won't have to dispose it manually.
Code would be like:
string fileName = "file.xslx";
int bufferSize = 4096;
using(var fileStream = System.IO.File.Create(fileName, bufferSize, System.IO.FileOptions.DeleteOnClose))
{
// now use that fileStream to save the xslx stream
}
// Get a random temporary file name w/ path:
string tempFile = Path.GetTempFileName();
// Open a FileStream to write to the file:
using (Stream fileStream = File.OpenWrite(tempFile)) { ... }
// Delete the file when you're done:
File.Delete(tempFile);
EDIT:
Sorry, maybe it's just me, but I could have sworn that when you initially posted the question you didn't have all that detail about a class implementing IDisposable, etc... anyways, I'm not really sure what you're asking in your (edited?) question. But this question: Any idea of how to save the stream to temp file and delete it on the end of use? is pretty straight-forward. Any number of google results will come back for ".NET C# Stream to File" or such.
I just suggest for creating file use Path.GetTempFileName(). but others depends on your usage senario, for example if you want to create it in your temp creator class and use it just there, it's good to use using keyword.
I would like to know the best way to create a simple html file using c#.
Is it using something like System.IO.File.Create?
Something like -
using (FileStream fs = new FileStream("test.htm", FileMode.Create))
{
using (StreamWriter w = new StreamWriter(fs, Encoding.UTF8))
{
w.WriteLine("<H1>Hello</H1>");
}
}
I'll say that File.WriteAllText is a stupid-proof way to write a text file for C# >= 3.5.
File.WriteAllText("myfile.htm", #"<html><body>Hello World</body></html>");
I'll even say that File.WriteAllLines is stupid-proof enough to write bigger html without fighting too much with string composition. But the "good" version is only for C# 4.0 (a little worse version is C# >= 2.0)
List<string> lines = new List<string>();
lines.Add("<html>");
lines.Add("<body>");
lines.Add("Hello World");
lines.Add("</body>");
lines.Add("</html>");
File.WriteAllLines("myfile.htm", lines);
// With C# 3.5
File.WriteAllLines("myfile.htm", lines.ToArray());
I would go with File.Create and then open a StreamWriter to that file if you dont have all the data when you create the file.
This is a example from MS that may help you
class Test
{
public static void Main()
{
string path = #"c:\temp\MyTest.txt";
// Create the file.
using (FileStream fs = File.Create(path, 1024))
{
Byte[] info = new UTF8Encoding(true).GetBytes("This is some text in the file.");
// Add some information to the file.
fs.Write(info, 0, info.Length);
}
// Open the stream and read it back.
using (StreamReader sr = File.OpenText(path))
{
string s = "";
while ((s = sr.ReadLine()) != null)
{
Console.WriteLine(s);
}
}
}
}
Have a look at the HtmlTextWriter class. For an example how to use this class, for example look at http://www.dotnetperls.com/htmltextwriter.
Reading and writing text files and MSDN info. HTML is just a simple text file with *.HTML extension ;)
Simply opening a file for writing (using File.OpenWrite() for example) will create the file if it does not yet exist.
If you have a look at http://msdn.microsoft.com/en-us/library/d62kzs03.aspx you can find an example of creating a file.
But how do you want to create the html file content? If that's just static then you can just write it to a file.. if you have to create the html on the fly you could use an ASPX file with the correct markup and use a Server.Execute to get the HTML as a string.
Yep, System.IO.File.Create(Path) will create your file just fine.
You can also use a filestream and write to it. Seems more handy to write a htm file