public static ChunkedFileHeader ReadFromMemoryMappedFile(MemoryMappedFile mmf, long maxChunks) { var header = new ChunkedFileHeader(); using (var headerAccessor = mmf.CreateViewStream(0, GetHeaderLengthInBytes(maxChunks))) { var buffer = new byte[sizeof(int)]; headerAccessor.Read(buffer, 0, buffer.Length); header.Version = BitConverter.ToInt32(buffer, 0); buffer = new byte[sizeof(long)]; headerAccessor.Read(buffer, 0, buffer.Length); header.MaxChunks = BitConverter.ToInt64(buffer, 0); if (header.MaxChunks == 0) { header.MaxChunks = maxChunks; } var chunksReceivedBytes = header.MaxChunks * sizeof(bool); buffer = new byte[chunksReceivedBytes]; headerAccessor.Read(buffer, 0, buffer.Length); header.ChunksReceived = new bool[chunksReceivedBytes / sizeof(bool)]; Buffer.BlockCopy(buffer, 0, header.ChunksReceived, 0, header.ChunksReceived.Length); } return(header); }
private void WriteChunkedFile(FileSystemInfo directory, TransportFileDelivery delivery, FileTransferProperties props, bool overwrite) { var fileName = delivery.TransportFile.Name; var filePath = GenerateUniqueFileName(Path.Combine(directory.FullName, fileName), overwrite); var uniqueTransferId = props.TransferId ?? 0; var tmpName = Path.ChangeExtension(filePath, $".{uniqueTransferId}.temp"); _log.LogDebug($"Processing chunk no. {props.ChunkNumber} - is last {props.LastChunk}"); if (filePath == null) { return; // only to make static code analysis happy. } try { long maxChunks; if (props.LastChunk) { maxChunks = props.ChunkNumber + 1; } else { maxChunks = props.FileSize / props.ChunkSize; maxChunks += props.FileSize % props.ChunkSize != 0 ? 1 : 0; } var headerSizeInBytes = ChunkedFileHeader.GetHeaderLengthInBytes(maxChunks); // Wait here to write to file var log = _log; // needed to make static code analysis happy (avoid using variable in and outside synchronization blocks warning) lock (string.Intern(tmpName)) { var tmpFileSize = headerSizeInBytes + props.FileSize; CheckTempFileConsistency(tmpName, uniqueTransferId, tmpFileSize); using (var fs = new FileStream(tmpName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read)) { using (var mmf = MemoryMappedFile.CreateFromFile(fs, null, tmpFileSize, MemoryMappedFileAccess.ReadWrite, HandleInheritability.Inheritable, false)) { var fileHeader = ChunkedFileHeader.ReadFromMemoryMappedFile(mmf, maxChunks); using (var accessor = mmf.CreateViewStream(headerSizeInBytes + props.ChunkOffset, 0)) { delivery.TransportFile.WriteTo(accessor); } fileHeader.ChunksReceived[props.ChunkNumber] = true; fileHeader.WriteToMemoryMappedFile(mmf); var isFileComplete = fileHeader.ChunksReceived.All(b => b); if (!isFileComplete) { return; } try { if (overwrite) { DeleteExistingFile(delivery, directory); } SaveCompletedFileContent(mmf, filePath, headerSizeInBytes, props.FileSize); log.LogInformation($"File written to {filePath}"); } catch (IOException e) { log.LogError($"Could not move file from {tmpName} to {filePath} because of {e.Message}"); throw; } } } File.Delete(tmpName); } FileReceived?.Invoke(filePath); } catch (IOException e) { _log.LogError($"Could not handle file {tmpName} -> {filePath}: '{e.Message}'"); } }