public async Task SendDeleteRequestsForFile(VirtualFileEntity file) { var users = _onlineUserRepository.UsersInNetworksOnline.FirstOrDefault(x => x.Key == file.NetworkId).Value; foreach (var user in users.ToList()) { var websocket = _webSocketRepository.GetAllActiveUsers().FirstOrDefault(x => x.Key == user.ToString()).Value; await SendRequestToDeleteFilePeaces(file.FilePieces, websocket); } }
public async Task UploadNewFileAsync(UploadFileDto dto) { var providers = GetScopeDbContext(); var _dbContext = providers.DbContext; /* * 1. split to file peaces by the default max file peace size * 2. Get all the users, who have enough free space to store one peace and online * 3. Store all file peace with these users by the following: * 4. Calculate redundancy value. e.g.: 10 active phones, 30 pieces, 10% redundancy * => all phone stores 3 by default, and 3 phone stores 3 more (10%) * 5. Store the additional file peaces with other phones (not the same duplicated) * 6. add the file to the db * 7. add the file peaces to the db * 8. save db */ // todo implement upload flow var uploaderUser = await _userService.GetUserById(dto.UserToken1); var chunks = dto.FileBytes.Chunk(_fileSettings.FilePeaceMaxSize); //var redundancy = (double) _fileSettings.RedundancyPercentage / 100; // e.g. 0.1 var freeUsers = await _userService.GetOnlineUsersInNetworkWhoHaveEnoughFreeSpace( _fileSettings.FilePeaceMaxSize, uploaderUser.NetworkId.Value); if (freeUsers.Count == 0) { throw new OperationFailedException( message: "There is no enough space to save this item", statusCode: HttpStatusCode.PreconditionFailed, webSocket: null); } var chunksAndIds = chunks.Select(x => (Bytes: x.Bytes, Id: Guid.NewGuid(), OrderNumber: x.OrderNumber)); chunks = null; var fileEntity = new VirtualFileEntity { NetworkId = uploaderUser.NetworkId.Value, Created = DateTime.Now, FileId = Guid.NewGuid(), FileName = dto.FileName, FileSize = dto.FileBytes.Length, LastModified = DateTime.Now, MimeType = dto.MimeType, UploadedBy = uploaderUser.Token1, IsConfirmed = false }; fileEntity = (await _dbContext.VirtualFile.AddAsync(fileEntity)).Entity; var tmpToAddIds = new List <(VirtualFilePieceEntity FIlePieceEntity, byte[] Bytes)>(); foreach (var chunksAndId in chunksAndIds) { var filePeaceEntity = new VirtualFilePieceEntity { FilePieceId = chunksAndId.Id, FileId = fileEntity.FileId, FilePieceSize = chunksAndId.Bytes.Length, OrderNumber = chunksAndId.OrderNumber, IsConfirmed = false }; var entity = (await _dbContext.VirtualFilePiece.AddAsync(filePeaceEntity)).Entity; tmpToAddIds.Add((entity, chunksAndId.Bytes)); } await _dbContext.SaveChangesAsync(); chunksAndIds = null; chunksAndIds = tmpToAddIds.Select(x => ( Bytes: x.Bytes, Id: x.FIlePieceEntity.FilePieceId, OrderNumber: x.FIlePieceEntity.OrderNumber)); var chunksAndIdsWithRedundancy = AddRedundancy(chunksAndIds.ToArray(), _fileSettings.RedundancyPercentage); chunksAndIds = null; var relation = new Dictionary <UserEntity, List <(byte[] bytes, Guid Id, int OrderNumber)> >(); foreach (var freeUser in freeUsers) { relation.Add(freeUser, new List <(byte[] Bytes, Guid Id, int OrderNumber)>()); } foreach (var fileBytesChunk in chunksAndIdsWithRedundancy) { var haveTheFewestAssociated = relation.FirstOrDefault(re => re.Value.Count == relation.Min(x => x.Value.Count)); int tryNumber = 0; while (haveTheFewestAssociated.Value.Contains(fileBytesChunk) && tryNumber < 1000) { var tmp = relation.ToArray(); haveTheFewestAssociated = tmp[_random.Next(tmp.Length)]; tryNumber++; } if (tryNumber < 1000) { haveTheFewestAssociated.Value.Add(fileBytesChunk); } } chunksAndIdsWithRedundancy = null; #region Debug //byte[] outp = new byte[fileEntity.FileSize]; //int i = 0; //foreach (var item in chunksAndIds.OrderBy(x => x.OrderNumber).ToList()) //{ // foreach (var itemByte in item.Bytes) // { // outp[i] = itemByte; // i++; // } //} //bool isEqal = true; //for (int j = 0; j < outp.Length; j++) //{ // if (dto.FileBytes[j] != outp[j]) // { // isEqal = false; // } //} //if (!isEqal) //{ // throw new ApplicationException("The two array size are not the same"); //} #endregion foreach (var relationItem in relation) { foreach (var filePeace in relationItem.Value) { //todo no need for await, because i want to send immediately to all phones and lock is applied _webSocketHandler .SendFilePeaceToUser( fileBytes: filePeace.bytes, user: relationItem.Key.Token1, filePeaceId: filePeace.Id, networkId: uploaderUser.NetworkId.Value); } } relation = null; providers.Scope.Dispose(); _logger.LogInformation("SUCCESS. All file has been prepared to sent."); }