/// <summary> /// Resolves a token to a FileReference, if the token exists for this user. /// The action should be called once the calling code can ensure the file is handled ok. /// </summary> /// <param name="mongoWrapper">For accessing the database</param> /// <param name="tokenId">The token ID to search for</param> /// <param name="userId">The user ID to correlate with the token</param> /// <returns>A Tuple of the FileReference and an async action to expire the token</returns> public static async Task <(FileReference, Func <Task>)> GetFileForReferenceToken(MongoWrapper mongoWrapper, string tokenId, ObjectId userId) { var tokenCollection = mongoWrapper.Database.GetCollection <DataReferenceToken <ConsumableData <ObjectId> > >(nameof(ReferenceToken)); var tokenFilterBuilder = new FilterDefinitionBuilder <DataReferenceToken <ConsumableData <ObjectId> > >(); var tokenFilter = tokenFilterBuilder.And ( GeneralUtils.NotDeactivated(tokenFilterBuilder), tokenFilterBuilder.Eq(t => t._id, tokenId), tokenFilterBuilder.Eq(t => t.UserId, userId) ); var tokenUpdateBuilder = new UpdateDefinitionBuilder <DataReferenceToken <ConsumableData <ObjectId> > >(); var tokenUpdate = tokenUpdateBuilder .Set(t => t.DeactivationDate, DateTime.UtcNow) .Set(t => t.AdditionalData.IsConsumed, true); DataReferenceToken <ConsumableData <ObjectId> > token = (await tokenCollection.FindAsync(tokenFilter, new FindOptions <DataReferenceToken <ConsumableData <ObjectId> >, DataReferenceToken <ConsumableData <ObjectId> > > { Limit = 1, AllowPartialResults = false, })).SingleOrDefault(); if (token == null) { throw new ValidationException("Token de Arquivo não encontrado!"); } var gridFsBucket = new GridFSBucket <ObjectId>(mongoWrapper.Database); var fileFilterBuilder = new FilterDefinitionBuilder <GridFSFileInfo <ObjectId> >(); var fileFilter = fileFilterBuilder.Eq(finfo => finfo.Id, token.AdditionalData.Data); var fileInfo = (await gridFsBucket.FindAsync(fileFilter, new GridFSFindOptions <ObjectId> { Limit = 1, })).SingleOrDefault(); if (fileInfo == null) { throw new ApplicationException("Arquivo não encontrado, mas havia um token ativado referenciando-o!"); } var fileReference = new FileReference { FileInfo = new Models.FileInfo { FileMetadata = BsonSerializer.Deserialize <FileMetadata>(fileInfo.Metadata), Size = fileInfo.Length }, _id = token.AdditionalData.Data }; return ( fileReference, async() => { var tokenUpdateTask = tokenCollection.UpdateOneAsync ( tokenFilter, tokenUpdate ); var userCollection = mongoWrapper.Database.GetCollection <User>(nameof(User)); var userFilterBuilder = new FilterDefinitionBuilder <User>(); var userFilter = userFilterBuilder.And ( GeneralUtils.NotDeactivated(userFilterBuilder), userFilterBuilder.Eq(u => u._id, userId) ); var userUpdateBuilder = new UpdateDefinitionBuilder <User>(); var userUpdate = userUpdateBuilder.Inc(u => u.FileBytesOccupied, fileReference.FileInfo.Size); var userUpdateTask = userCollection.UpdateOneAsync ( userFilter, userUpdate ); await tokenUpdateTask; await userUpdateTask; } ); }
public async Task <dynamic> Put() { var contentType = Request.Headers["Content-Type"]; FileType?fileType = FileTypeFromMime(contentType); if (!fileType.HasValue) { throw new ValidationException("Tipo de arquivo desconhecido! Tipo: " + contentType); } var generatedFileId = ObjectId.GenerateNewId(); var gridFsBucket = new GridFSBucket <ObjectId>(MongoWrapper.Database); var uploadTask = gridFsBucket.UploadFromStreamAsync(generatedFileId, generatedFileId.ToString(), Request.Body, new GridFSUploadOptions { Metadata = new FileMetadata { ContentType = contentType, FileType = fileType.Value } .ToBsonDocument() }); var id = this.GetCurrentUserId(); var confirmationCollection = MongoWrapper.Database.GetCollection <ReferenceToken>(typeof(ReferenceToken).Name); var generatedToken = await GeneralUtils.GenerateRandomBase64(256); var token = new DataReferenceToken <ConsumableData <ObjectId> >() { UserId = new ObjectId(id), TokenType = TokenType.FileUpload, _id = generatedToken, AdditionalData = new ConsumableData <ObjectId> { Data = generatedFileId, IsConsumed = false, }, // Auto-Deletion allowed 1 day from now. // TODO: Implement job that does auto-deletion // TODO: How to check for FileReference? Can't delete file if it is already used, // in those cases we should delete only the token! // TODO: Maybe a Boolean in AdditionalData which says if this token is being used or not: // If it is, the job will delete only the token, and not the file as well. Sounds good. // Boolean is implemented, only the job is left. DeactivationDate = DateTime.UtcNow + TimeSpan.FromHours(8) }; var insertTokenTask = confirmationCollection.InsertOneAsync(token); using (var session = await MongoWrapper.MongoClient.StartSessionAsync()) { session.StartTransaction(); try { await insertTokenTask; await uploadTask; } catch (Exception e) { Logger.LogError("Error while uploading user resource", e); await session.AbortTransactionAsync(); throw; } await session.CommitTransactionAsync(); } return(new ResponseBody { Code = ResponseCode.GenericSuccess, Data = generatedToken, Message = "Arquivo enviado com sucesso!", Success = true }); }