/// <summary> /// Fetch session descriptor with metadata (and store it in cache). /// </summary> /// <param name="sessionId">Session identifier.</param> /// <exception cref="ObjectNotFoundException">Session not found.</exception> /// <exception cref="SessionExpiredException">Session has expired.</exception> /// <exception cref="S3Exception">S3 error.</exception> /// <returns>Session descriptor with metadata.</returns> public async Task <(SessionDescriptor SessionDescriptor, AuthorInfo AuthorInfo, DateTime ExpiresAt)> GetSessionDescriptor(Guid sessionId) { var result = await _memoryCache.GetOrCreateAsync( sessionId, async entry => { try { using (var objectResponse = await _cephS3Client.GetObjectAsync(_filesBucketName, sessionId.AsS3ObjectKey(Tokens.SessionPostfix))) { var metadataWrapper = MetadataCollectionWrapper.For(objectResponse.Metadata); var expiresAt = metadataWrapper.Read <DateTime>(MetadataElement.ExpiresAt); var author = metadataWrapper.Read <string>(MetadataElement.Author); var authorLogin = metadataWrapper.Read <string>(MetadataElement.AuthorLogin); var authorName = metadataWrapper.Read <string>(MetadataElement.AuthorName); string json; using (var reader = new StreamReader(objectResponse.ResponseStream, Encoding.UTF8)) { json = reader.ReadToEnd(); } var sessionDescriptor = JsonConvert.DeserializeObject <SessionDescriptor>(json, SerializerSettings.Default); var tuple = (SessionDescriptor : sessionDescriptor, AuthorInfo : new AuthorInfo(author, authorLogin, authorName), ExpiresAt : expiresAt); entry.SetValue(tuple) .SetAbsoluteExpiration(expiresAt); return(tuple); } } catch (AmazonS3Exception ex) when(ex.StatusCode == HttpStatusCode.NotFound) { throw new ObjectNotFoundException($"Session '{sessionId}' does not exist"); } catch (Exception ex) { throw new S3Exception(ex); } }); if (SessionDescriptor.IsSessionExpired(result.ExpiresAt)) { throw new SessionExpiredException(sessionId, result.ExpiresAt); } return(result); }
public async Task <string> CompleteMultipartUpload(MultipartUploadSession uploadSession) { var uploadKey = uploadSession.SessionId.AsS3ObjectKey(uploadSession.FileKey); var partETags = uploadSession.Parts.Select(x => new PartETag(x.PartNumber, x.Etag)).ToList(); var uploadResponse = await _cephS3Client.CompleteMultipartUploadAsync( new CompleteMultipartUploadRequest { BucketName = _filesBucketName, Key = uploadKey, UploadId = uploadSession.UploadId, PartETags = partETags }); uploadSession.Complete(); if (SessionDescriptor.IsSessionExpired(uploadSession.SessionExpiresAt)) { throw new SessionExpiredException(uploadSession.SessionId, uploadSession.SessionExpiresAt); } try { using (var getResponse = await _cephS3Client.GetObjectAsync(_filesBucketName, uploadKey)) { var memoryStream = new MemoryStream(); using (getResponse.ResponseStream) { getResponse.ResponseStream.CopyTo(memoryStream); memoryStream.Position = 0; } using (memoryStream) { var sessionDescriptor = uploadSession.SessionDescriptor; var elementDescriptor = uploadSession.ElementDescriptor; EnsureFileContentIsValid( elementDescriptor.TemplateCode, elementDescriptor.Type, elementDescriptor.Constraints.For(sessionDescriptor.Language), memoryStream, uploadSession.UploadedFileMetadata); } var metadataWrapper = MetadataCollectionWrapper.For(getResponse.Metadata); var fileName = metadataWrapper.Read <string>(MetadataElement.Filename); var fileExtension = Path.GetExtension(fileName).ToLowerInvariant(); var fileKey = Path.ChangeExtension(uploadSession.SessionId.AsS3ObjectKey(uploadResponse.ETag), fileExtension); var copyRequest = new CopyObjectRequest { ContentType = uploadSession.UploadedFileMetadata.ContentType, SourceBucket = _filesBucketName, SourceKey = uploadKey, DestinationBucket = _filesBucketName, DestinationKey = fileKey, MetadataDirective = S3MetadataDirective.REPLACE, CannedACL = S3CannedACL.PublicRead }; foreach (var metadataKey in getResponse.Metadata.Keys) { copyRequest.Metadata.Add(metadataKey, getResponse.Metadata[metadataKey]); } await _cephS3Client.CopyObjectAsync(copyRequest); _uploadedBinariesMetric.Inc(); _memoryCache.Set(fileKey, new BinaryMetadata(fileName, getResponse.ContentLength), uploadSession.SessionExpiresAt); return(fileKey); } } finally { await _cephS3Client.DeleteObjectAsync(_filesBucketName, uploadKey); } }