/// <inheritdoc /> 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[ByteChunkSize]; 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); } }
private InternalFileRep Create(InternalFileId fileId, string extension) { var fileName = fileId.FileId; if (!string.IsNullOrEmpty(extension)) { fileName += "." + extension; } return(new InternalFileRep(fileId.FileId, System.IO.Path.Combine(_directoryPath, fileName))); }
/// <inheritdoc /> public async Task <string> CreateFileAsync(long uploadLength, string metadata, CancellationToken cancellationToken) { var fileId = new InternalFileId(); File.Create(_fileRepFactory.Data(fileId).Path).Dispose(); if (uploadLength != -1) { await SetUploadLengthAsync(fileId.FileId, uploadLength, cancellationToken); } _fileRepFactory.Metadata(fileId).Write(metadata); return(fileId.FileId); }
/// <inheritdoc /> 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)); }
/// <inheritdoc /> 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, ByteChunkSize); } } } // ReSharper disable once InvertIf if (_deletePartialFilesOnConcat) { await Task.WhenAll(partialInternalFileReps.Select(f => DeleteFileAsync(f.FileId, cancellationToken))); } return(fileId); }
/// <inheritdoc /> 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.ReadWrite, FileShare.Read)) { var chunkPositionFile = _fileRepFactory.ChunkStartPosition(internalFileId); var chunkStartPosition = chunkPositionFile.ReadFirstLineAsLong(true, 0); var calculateSha1 = dataStream.CalculateSha1(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)); }
public InternalFileRep ChunkStartPosition(InternalFileId fileId) => Create(fileId, "chunkstart");
public InternalFileRep ChunkComplete(InternalFileId fileId) => Create(fileId, "chunkcomplete");
public InternalFileRep Expiration(InternalFileId fileId) => Create(fileId, "expiration");
public InternalFileRep Metadata(InternalFileId fileId) => Create(fileId, "metadata");
public InternalFileRep UploadConcat(InternalFileId fileId) => Create(fileId, "uploadconcat");
public InternalFileRep UploadLength(InternalFileId fileId) => Create(fileId, "uploadlength");
public InternalFileRep Data(InternalFileId fileId) => Create(fileId, "");