public async Task <long> AppendDataAsync(string fileId, Stream stream, CancellationToken cancellationToken) { var internalFileId = new InternalFileId(fileId); long bytesWritten = 0; var uploadLength = await GetUploadLengthAsync(fileId, cancellationToken); using (var file = _fileRepFactory.Data(internalFileId).GetStream(FileMode.Append, FileAccess.Write, FileShare.None)) { var fileLength = file.Length; if (uploadLength == fileLength) { return(0); } var chunkStart = _fileRepFactory.ChunkStartPosition(internalFileId); var chunkComplete = _fileRepFactory.ChunkComplete(internalFileId); if (chunkComplete.Exist()) { chunkStart.Delete(); chunkComplete.Delete(); } if (!chunkStart.Exist()) { chunkStart.Write(fileLength.ToString()); } int bytesRead; do { if (cancellationToken.IsCancellationRequested) { break; } var buffer = new byte[StreamExtensions.BufferSize]; bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); fileLength += bytesRead; if (fileLength > uploadLength) { throw new TusStoreException($"Stream contains more data than the file's upload length. Stream data: {fileLength}, upload length: {uploadLength}."); } await file.WriteAsync(buffer, 0, bytesRead); bytesWritten += bytesRead; } while (bytesRead != 0); // Chunk is complete. Mark it as complete. chunkComplete.Write("1"); return(bytesWritten); } }
public async Task <string> CreateFileAsync(long uploadLength, string metadata, CancellationToken cancellationToken) { var fileId = new InternalFileId(); _fileProvider.Create(_fileRepFactory.Data(fileId).Path).Dispose(); if (uploadLength != -1) { await SetUploadLengthAsync(fileId.FileId, uploadLength, cancellationToken); } _fileRepFactory.Metadata(fileId).Write(metadata); return(fileId.FileId); }
public Task DeleteFileAsync(string fileId, CancellationToken cancellationToken) { var internalFileId = new InternalFileId(fileId); return(Task.Run(() => { _fileRepFactory.Data(internalFileId).Delete(); _fileRepFactory.UploadLength(internalFileId).Delete(); _fileRepFactory.Metadata(internalFileId).Delete(); _fileRepFactory.UploadConcat(internalFileId).Delete(); _fileRepFactory.Expiration(internalFileId).Delete(); _fileRepFactory.ChunkStartPosition(internalFileId).Delete(); _fileRepFactory.ChunkComplete(internalFileId).Delete(); }, cancellationToken)); }
public async Task <string> CreateFinalFileAsync(string[] partialFiles, string metadata, CancellationToken cancellationToken) { var partialInternalFileReps = partialFiles.Select(f => { var partialData = _fileRepFactory.Data(new InternalFileId(f)); if (!partialData.Exist()) { throw new TusStoreException($"File {f} does not exist"); } return(partialData); }).ToArray(); var length = partialInternalFileReps.Sum(f => f.GetLength()); var fileId = await CreateFileAsync(length, metadata, cancellationToken); var internalFileId = new InternalFileId(fileId); _fileRepFactory.UploadConcat(internalFileId).Write(new FileConcatFinal(partialFiles).GetHeader()); using (var finalFile = _fileRepFactory.Data(internalFileId).GetStream(FileMode.Open, FileAccess.Write, FileShare.None)) { foreach (var partialFile in partialInternalFileReps) { using (var partialStream = partialFile.GetStream(FileMode.Open, FileAccess.Read, FileShare.Read)) { await partialStream.CopyToAsync(finalFile, StreamExtensions.BufferSize); } } } // ReSharper disable once InvertIf if (_deletePartialFilesOnConcat) { await Task.WhenAll(partialInternalFileReps.Select(f => DeleteFileAsync(f.FileId, cancellationToken))); } return(fileId); }
public Task <bool> VerifyChecksumAsync(string fileId, string algorithm, byte[] checksum, CancellationToken cancellationToken) { bool valid; var internalFileId = new InternalFileId(fileId); using (var dataStream = _fileRepFactory.Data(internalFileId).GetStream(FileMode.Open, FileAccess.Read, FileShare.Read)) { var chunkPositionFile = _fileRepFactory.ChunkStartPosition(internalFileId); var chunkStartPosition = chunkPositionFile.ReadFirstLineAsLong(true, 0); var calculateSha1 = CalculateSha1(dataStream, chunkStartPosition); valid = checksum.SequenceEqual(calculateSha1); if (!valid) { dataStream.Seek(0, SeekOrigin.Begin); dataStream.SetLength(chunkStartPosition); chunkPositionFile.Delete(); _fileRepFactory.ChunkComplete(internalFileId).Delete(); } } return(Task.FromResult(valid)); }
InternalSftpFileRep Create(InternalFileId fileId, string extension) { var fileName = $"{fileId.FileId}{(string.IsNullOrEmpty(extension) ? string.Empty : $".{extension}")}";
public InternalSftpFileRep ChunkStartPosition(InternalFileId fileId) => Create(fileId, "chunkstart");
public InternalSftpFileRep ChunkComplete(InternalFileId fileId) => Create(fileId, "chunkcomplete");
public InternalSftpFileRep Expiration(InternalFileId fileId) => Create(fileId, "expiration");
public InternalSftpFileRep Metadata(InternalFileId fileId) => Create(fileId, "metadata");
public InternalSftpFileRep UploadConcat(InternalFileId fileId) => Create(fileId, "uploadconcat");
public InternalSftpFileRep UploadLength(InternalFileId fileId) => Create(fileId, "uploadlength");
public InternalSftpFileRep Data(InternalFileId fileId) => Create(fileId, "");