public async Task DoesNotUploadWhenThereIsExistingFileWithSameHash() { // arrange var fileName = "foo.txt"; string filePath = Path.Combine(Path.GetTempPath(), fileName); File.WriteAllText(filePath, "123"); long fullFileSize = new FileInfo(filePath).Length; var hashForFile = new MD5FileHasher().CalculateHashForFile(filePath); int resourceId = 1; var checkFileResult = new CheckFileResult { StatusCode = HttpStatusCode.NoContent, LastModified = DateTimeOffset.UtcNow, FileNameOnServer = fileName, HashForFileOnServer = hashForFile }; // Have CheckFileAsync return NoContent with a hash mockFileService.Setup( service => service.CheckFileAsync(resourceId)) .ReturnsAsync(checkFileResult); // act await this.classUnderTest.UploadFileAsync(resourceId, filePath, this.cancellationToken); // assert mockFileService.Verify( service => service.CheckFileAsync(1), Times.Once); mockFileService.Verify( service => service.SetUploadedAsync(1), Times.Once); mockFileService.Verify( service => service.CreateNewUploadSessionAsync(resourceId), Times.Never); mockFileService.Verify( service => service.UploadStreamAsync(resourceId, It.IsAny <Guid>(), It.IsAny <Stream>(), It.IsAny <FilePart>(), fileName, fullFileSize, It.IsAny <int>()), Times.Never); mockFileService.Verify( service => service.CommitAsync(resourceId, It.IsAny <Guid>(), fileName, hashForFile, fullFileSize, It.IsAny <IList <FilePart> >()), Times.Never); }
private async Task <UploaderCheckFileResult> CheckIfFileExistsOnServerAsync(IFileServiceClient fileServiceClient, int resourceId, string filePath, long fullFileSize) { if (fileServiceClient == null) { throw new ArgumentNullException(nameof(fileServiceClient)); } if (resourceId <= 0) { throw new ArgumentOutOfRangeException(nameof(resourceId)); } var result = await fileServiceClient.CheckFileAsync(resourceId); switch (result.StatusCode) { case HttpStatusCode.NoContent: { // if server has the file and the filename is the same then do a hash check to see if file needs uploading var fileNameOnServer = Path.GetFileName(result.FileNameOnServer); if (fileNameOnServer != null && fileNameOnServer.Equals(Path.GetFileName(filePath)) && result.HashForFileOnServer != null) { OnCalculatingHash(new CalculatingHashEventArgs(resourceId, filePath, fullFileSize)); var hashForFile = new MD5FileHasher().CalculateHashForFile(filePath); if (hashForFile == result.HashForFileOnServer && Path.GetFileName(filePath) == fileNameOnServer) { OnFileChecked(new FileCheckedEventArgs(resourceId, true, hashForFile, result.HashForFileOnServer, result.LastModified, fileNameOnServer, true)); return(new UploaderCheckFileResult { FileNeedsUploading = false, HashForFile = hashForFile, HashForFileOnServer = result.HashForFileOnServer, FileNameOnServer = fileNameOnServer }); } else { OnFileChecked(new FileCheckedEventArgs(resourceId, true, hashForFile, result.HashForFileOnServer, result.LastModified, fileNameOnServer, false)); return(new UploaderCheckFileResult { FileNeedsUploading = true, HashForFile = hashForFile, HashForFileOnServer = result.HashForFileOnServer, FileNameOnServer = fileNameOnServer }); } } break; } case HttpStatusCode.NotFound: // this is acceptable response if the file does not exist on the server OnFileChecked(new FileCheckedEventArgs(resourceId, false, null, null, null, null, false)); return(new UploaderCheckFileResult { FileNeedsUploading = true, }); default: OnUploadError(new UploadErrorEventArgs(resourceId, result.FullUri, result.StatusCode.ToString(), result.Error)); throw new FileUploaderException(result.FullUri, result.StatusCode.ToString(), result.Error); } return(new UploaderCheckFileResult { FileNeedsUploading = true }); }
public async Task <IList <FilePart> > SplitFile(string filePath, string fileName, long chunkSizeInBytes, long maxFileSizeInMegabytes, Func <Stream, FilePart, Task> fnActionForStream) { if (string.IsNullOrWhiteSpace(filePath)) { throw new ArgumentNullException(nameof(filePath)); } if (string.IsNullOrWhiteSpace(fileName)) { throw new ArgumentNullException(nameof(fileName)); } if (chunkSizeInBytes <= 0) { throw new ArgumentOutOfRangeException(nameof(chunkSizeInBytes)); } if (maxFileSizeInMegabytes <= 0) { throw new ArgumentOutOfRangeException(nameof(maxFileSizeInMegabytes)); } // first check tht file is not too big var fileSize = new FileInfo(filePath).Length; var maxFileSizeInBytes = (maxFileSizeInMegabytes * 1024 * 1024); if (fileSize > maxFileSizeInBytes) { throw new InvalidOperationException($"File {filePath} is too big {fileSize} bytes while server allows {maxFileSizeInBytes}"); } var md5FileHasher = new MD5FileHasher(); // set the size of file chunk we are going to split into long bufferChunkSize = chunkSizeInBytes; var fileParts = new List <FilePart>(); // open the file to read it into chunks using (FileStream fullFileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { int filePartCount = 0; byte[] data = new byte[bufferChunkSize]; // scan through the file, and each time we get enough data to fill a chunk, write out that file while (fullFileStream.Position < fullFileStream.Length) { long startOffset = fullFileStream.Position; var bytesRead = await fullFileStream.ReadAsync(data, 0, Convert.ToInt32(bufferChunkSize)); using (var memoryStream = new MemoryStream(data, 0, Convert.ToInt32(bytesRead))) { memoryStream.Seek(0, SeekOrigin.Begin); var filePart = new FilePart { Id = filePartCount, Offset = startOffset, Size = memoryStream.Length, Hash = md5FileHasher.CalculateHashForStream(memoryStream), }; await fnActionForStream(memoryStream, filePart); fileParts.Add(filePart); } // file written, loop for next chunk filePartCount++; } } return(fileParts); }
public async Task UploadFileIsSuccessful() { // arrange var fileName = "foo.txt"; string filePath = Path.Combine(Path.GetTempPath(), fileName); File.WriteAllText(filePath, "123"); long fullFileSize = new FileInfo(filePath).Length; var hashForFile = new MD5FileHasher().CalculateHashForFile(filePath); int resourceId = 1; var checkFileResult = new CheckFileResult { StatusCode = HttpStatusCode.NoContent, LastModified = DateTimeOffset.UtcNow, FileNameOnServer = fileName, HashForFileOnServer = "MyHash" }; mockFileService.Setup( service => service.CheckFileAsync(resourceId)) .ReturnsAsync(checkFileResult); var sessionId = Guid.NewGuid(); var uploadSession = new UploadSession { SessionId = sessionId, FileUploadChunkSizeInBytes = 1, FileUploadMaxFileSizeInMegabytes = 10 }; var createSessionResult = new CreateSessionResult { StatusCode = HttpStatusCode.OK, Session = uploadSession }; mockFileService.Setup( service => service.CreateNewUploadSessionAsync(resourceId)) .ReturnsAsync(createSessionResult); var fileSplitter = new FileSplitter(); var countOfFileParts = fileSplitter.GetCountOfFileParts(createSessionResult.Session.FileUploadChunkSizeInBytes, fullFileSize); var uploadStreamResult = new UploadStreamResult { StatusCode = HttpStatusCode.OK, PartsUploaded = 1 }; mockFileService.Setup( service => service.UploadStreamAsync(resourceId, sessionId, It.IsAny <Stream>(), It.IsAny <FilePart>(), fileName, fullFileSize, countOfFileParts)) .ReturnsAsync(uploadStreamResult); var commitResult = new CommitResult { StatusCode = HttpStatusCode.OK, Session = uploadSession }; mockFileService.Setup( service => service.CommitAsync(resourceId, sessionId, fileName, hashForFile, fullFileSize, It.IsAny <IList <FilePart> >())) .ReturnsAsync(commitResult); // act await this.classUnderTest.UploadFileAsync(resourceId, filePath, this.cancellationToken); // assert mockFileService.Verify( service => service.CheckFileAsync(1), Times.Once); mockFileService.Verify( service => service.CreateNewUploadSessionAsync(resourceId), Times.Once); mockFileService.Verify( service => service.UploadStreamAsync(resourceId, sessionId, It.IsAny <Stream>(), It.IsAny <FilePart>(), fileName, fullFileSize, countOfFileParts), Times.Exactly(countOfFileParts)); mockFileService.Verify( service => service.CommitAsync(resourceId, sessionId, fileName, hashForFile, fullFileSize, It.IsAny <IList <FilePart> >()), Times.Once); }