//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ private void EnsurePieceStream(bool isLastPiece) { if (_pieceStream != null) { // Normally, the pieces are actually closed automatically for us by the // underlying ZipIO logic, but in the case of the last piece (when we // are called by our own Dispose(bool) method) we must close it explicitly. if (isLastPiece) { _pieceStream.Close(); } // We detect that the stream has been closed by inspecting the CanWrite property // since this is guaranteed not to throw even when the stream is disposed. if (!_pieceStream.CanWrite) { // increment our piece number so we can generate the correct // one below checked { ++_currentPieceNumber; } // release it to trigger the new piece creation below _pieceStream = null; } } if (_pieceStream == null) { string pieceName = PieceNameHelper.CreatePieceName( _partName, _currentPieceNumber, isLastPiece); string pieceZipName = ZipPackage.GetZipItemNameFromOpcName(pieceName); ZipFileInfo zipFileInfo = _archive.AddFile(pieceZipName, _compressionMethod, _deflateOption); // We've just created the file, so the mode can only be Create, not CreateNew. // (At least, this is part of ZipFileInfo's belief system.) _pieceStream = zipFileInfo.GetStream(FileMode.Create, _access); } }
/// <summary> /// Return true and create a PieceInfo if the name in the input ZipFileInfo parses /// as a piece name. /// </summary> /// <remarks> /// No Uri validation is carried out at this level. All that is checked is valid piece /// syntax. So the _prefixName returned as part of the PieceInfo will not necessarily /// a part name. For example, it could be the name of the content type stream. /// </remarks> internal static bool TryCreatePieceInfo(ZipFileInfo zipFileInfo, out PieceInfo pieceInfo) { Invariant.Assert(zipFileInfo != null); pieceInfo = null; // Try to parse as a piece name. PieceNameInfo pieceNameConstituents; bool result = PieceNameHelper.TryParseAsPieceName(zipFileInfo.Name, out pieceNameConstituents); // Return the result and the output parameter. if (result) { pieceInfo = new PieceInfo(zipFileInfo, pieceNameConstituents.PartUri, pieceNameConstituents.PrefixName, pieceNameConstituents.PieceNumber, pieceNameConstituents.IsLastPiece); } return(result); }
/// <summary> /// If the logical end precedes the physical end, delete invalidated pieces /// and rename the logical end to a name containing ".last". /// </summary> private void UpdatePhysicalEndIfNecessary() { if (!_logicalEndPrecedesPhysicalEnd) { return; } // Delete invalidated pieces. int pieceNumber = _lastPieceIndex + 1; while (pieceNumber < _sortedPieceInfoList.Count) { _zipArchive.DeleteFile(_sortedPieceInfoList[pieceNumber].ZipFileInfo.Name); pieceNumber++; } _sortedPieceInfoList.RemoveRange(_lastPieceIndex + 1, _sortedPieceInfoList.Count - (_lastPieceIndex + 1)); // Since there is no rename in Zip I/O, getting the last piece to have .last // in its name necessarily involves creating a new piece. The simplest and most // effective solution consists in adding an empty terminal piece. // Number of the new physical last piece. int lastPiece = _lastPieceIndex + 1; // Record the compression parameters of the first piece to apply them to the new piece. // (Though this part will be created as empty, it may grow later.) ZipFileInfo firstPieceInfo = _sortedPieceInfoList[0].ZipFileInfo; CompressionMethodEnum compressionMethod = firstPieceInfo.CompressionMethod; DeflateOptionEnum deflateOption = firstPieceInfo.DeflateOption; // We have to special-case SetLength(0), because in that case, there is no nonempty // piece at all; and only the last piece is allowed to be empty. if (_lastPieceIndex == 0 && _pieceStreamInfoList[0].Stream.Length == 0) { _zipArchive.DeleteFile(firstPieceInfo.Name); // The list of piece descriptors now becomes totally empty. // This temporarily violates an invariant that should obtain again // on exiting this function. _indexOfLastPieceStreamInfoAccessed = -1; //Remove all the items in the list _pieceStreamInfoList.Clear(); lastPiece = 0; // Create "[0].last.piece" } string newLastPieceFileName = PieceNameHelper.CreatePieceName( _sortedPieceInfoList[0].PrefixName, lastPiece, true /* last piece */); ZipFileInfo newLastPieceInfo = _zipArchive.AddFile(newLastPieceFileName, compressionMethod, deflateOption); _lastPieceIndex = lastPiece; //We need to update the _sortedPieceInfoList with this new last piece information _sortedPieceInfoList.Add( new PieceInfo( newLastPieceInfo, _sortedPieceInfoList[0].PartUri, _sortedPieceInfoList[0].PrefixName, _lastPieceIndex, true /* last piece */)); // If we have been creating [0].last.piece, create a stream descriptor for it. // (In other cases, create on demand, as usual.) if (lastPiece == 0) { Stream pieceStream = newLastPieceInfo.GetStream(_fileMode, _fileAccess); _indexOfLastPieceStreamInfoAccessed = 0; //The list should be empty at this point Invariant.Assert(_pieceStreamInfoList.Count == 0); _pieceStreamInfoList.Add(new PieceStreamInfo(pieceStream, 0 /*startOffset*/)); } // Mark update complete. _logicalEndPrecedesPhysicalEnd = false; }