/* * 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.")); } } } }