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
            });
        }
示例#3
0
        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);
        }
示例#4
0
        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);
        }