Exemple #1
0
        public List <Guid> GetUsersWhoHaveTheFile(VirtualFilePieceEntity filePiece)
        {
            var response = new List <Guid>();

            lock (_lockObjectGetUsersWhoHaveTheFile)
            {
                foreach (var user in FilePiecesInUsersOnline)
                {
                    var fp = user.Value.ToList();

                    if (fp.Contains(filePiece.FilePieceId))
                    {
                        response.Add(user.Key);
                    }
                }
            }

            return(response);
        }
Exemple #2
0
        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.");
        }