/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.IO;
using System.IO.Compression;
using System.Text;
using QuantConnect.Logging;
using static System.FormattableString;
namespace QuantConnect
{
///
/// Provides an implementation of to write to a zip file
///
public class ZipStreamWriter : TextWriter
{
private readonly string _filename;
private readonly string _zipEntry;
private readonly string _tempFilename;
private ZipArchive _archive;
private StreamWriter _writer;
///
/// When overridden in a derived class, returns the character encoding in which the output is written.
///
///
/// The character encoding in which the output is written.
///
/// 1
public override Encoding Encoding
{
get { return Encoding.Default; }
}
///
/// Initializes a new instance of the class
///
/// The output zip file name
/// The file name in the zip file
public ZipStreamWriter(string filename, string zipEntry)
{
_filename = filename;
_zipEntry = zipEntry;
_tempFilename = $"{_filename}.tmp";
File.Delete(_tempFilename);
if (!File.Exists(filename))
{
_archive = ZipFile.Open(_tempFilename, ZipArchiveMode.Create);
_writer = new StreamWriter(_archive.CreateEntry(zipEntry).Open());
}
else
{
File.Copy(filename, _tempFilename);
_archive = ZipFile.Open(_tempFilename, ZipArchiveMode.Update);
var entry = _archive.GetEntry(zipEntry) ?? _archive.CreateEntry(zipEntry);
_writer = new StreamWriter(entry.Open());
_writer.BaseStream.Seek(0L, SeekOrigin.End);
}
}
///
/// Writes a character to the text string or stream.
///
/// The character to write to the text stream.
/// The is closed.
/// An I/O error occurs.
/// 1
public override void Write(char value)
{
_writer.Write(value);
}
///
/// Writes a string followed by a line terminator to the text string or stream.
///
/// The string to write. If is null, only the line terminator is written.
/// The is closed.
/// An I/O error occurs.
/// 1
public override void WriteLine(string value)
{
_writer.WriteLine(value);
}
///
/// Clears all buffers for the current writer and causes any buffered data to be written to the underlying device.
///
public override void Flush()
{
_writer.Flush();
_writer.Dispose();
_archive.Dispose();
CopyTempFile(5, throwOnFailure: false);
_archive = ZipFile.Open(_tempFilename, ZipArchiveMode.Update);
var entry = _archive.GetEntry(_zipEntry) ?? _archive.CreateEntry(_zipEntry);
_writer = new StreamWriter(entry.Open());
_writer.BaseStream.Seek(0L, SeekOrigin.End);
}
///
/// Releases the unmanaged resources used by the and optionally releases the managed resources.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected override void Dispose(bool disposing)
{
if (_writer == null || !disposing)
return;
_writer.Flush();
_writer.Close();
_writer.Dispose();
_archive.Dispose();
CopyTempFile(50, throwOnFailure: true);
if (File.Exists(_tempFilename))
{
File.Delete(_tempFilename);
}
}
private void CopyTempFile(int attempts, bool throwOnFailure)
{
if (!File.Exists(_tempFilename)) return;
do
{
try
{
File.Copy(_tempFilename, _filename, true);
return;
}
catch (Exception err)
{
Log.Error(err);
}
}
while (--attempts > 0);
if (throwOnFailure)
{
throw new InvalidOperationException(Invariant($"Unable to save file: {_filename} after {attempts} attempts."));
}
}
}
}