public async Task UploadFilePart(MultipartUploadSession uploadSession, Stream inputStream, int templateCode) { if (SessionDescriptor.IsSessionExpired(uploadSession.SessionExpiresAt)) { throw new SessionExpiredException(uploadSession.SessionId, uploadSession.SessionExpiresAt); } if (uploadSession.NextPartNumber == 1) { var sessionDescriptor = uploadSession.SessionDescriptor; var elementDescriptor = uploadSession.ElementDescriptor; EnsureFileHeaderIsValid( templateCode, uploadSession.ElementDescriptor.Type, elementDescriptor.Constraints.For(sessionDescriptor.Language), inputStream, uploadSession.UploadedFileMetadata); } var key = uploadSession.SessionId.AsS3ObjectKey(uploadSession.FileKey); inputStream.Position = 0; var response = await _cephS3Client.UploadPartAsync( new UploadPartRequest { BucketName = _filesBucketName, Key = key, UploadId = uploadSession.UploadId, InputStream = inputStream, PartNumber = uploadSession.NextPartNumber }); uploadSession.AddPart(response.ETag); }
public async Task AbortMultipartUpload(MultipartUploadSession uploadSession) { if (!uploadSession.IsCompleted) { var key = uploadSession.SessionId.AsS3ObjectKey(uploadSession.FileKey); await _cephS3Client.AbortMultipartUploadAsync(_filesBucketName, key, uploadSession.UploadId); } }
/// <summary> /// Fetch file for created session. /// </summary> /// <param name="sessionId">Session identifier.</param> /// <param name="templateCode">Element's template code.</param> /// <param name="fetchParameters">Fetch parameters.</param> /// <exception cref="InvalidTemplateException">Specified template code does not refer to binary element.</exception> /// <exception cref="InvalidFetchUrlException">Fetch URL is invalid.</exception> /// <exception cref="MissingFilenameException">Filename is not specified.</exception> /// <exception cref="ObjectNotFoundException">Session or template has not been found.</exception> /// <exception cref="SessionExpiredException">Specified session is expired.</exception> /// <exception cref="S3Exception">Error making request to S3.</exception> /// <exception cref="InvalidBinaryException">Binary does not meet template's constraints.</exception> /// <exception cref="FetchFailedException">Fetch request failed.</exception> /// <returns>Fetched file key.</returns> public async Task <string> FetchFile(Guid sessionId, int templateCode, FetchParameters fetchParameters) { if (!TryCreateUri(fetchParameters, out var fetchUri)) { throw new InvalidFetchUrlException( $"Fetch URI must be a valid absolute URI with '{Uri.UriSchemeHttp}' or '{Uri.UriSchemeHttps}' scheme"); } MultipartUploadSession uploadSession = null; var(sessionDescriptor, _, expiresAt) = await _sessionStorageReader.GetSessionDescriptor(sessionId); // Firstly ensure that specified session exists try { var(stream, mediaType) = await _fetchClient.FetchAsync(fetchUri); var fileMetadata = new GenericUploadedFileMetadata(fetchParameters.FileName, mediaType, stream.Length); uploadSession = await InitiateMultipartUploadInternal(sessionId, templateCode, fileMetadata, sessionDescriptor, expiresAt); await UploadFilePart(uploadSession, stream, templateCode); var fetchedFileKey = await CompleteMultipartUpload(uploadSession); return(fetchedFileKey); } catch (FetchResponseTooLargeException ex) { throw new InvalidBinaryException(templateCode, new BinaryTooLargeError(ex.ContentLength)); } catch (FetchRequestException ex) { throw new FetchFailedException($"Fetch request failed with status code {ex.StatusCode} and content: {ex.Message}"); } catch (HttpRequestException ex) { throw new FetchFailedException($"Fetch request failed: {ex.Message}"); } catch (TimeoutRejectedException) { throw new FetchFailedException("Fetch request failed: request timeout exceeded"); } catch (FetchResponseContentTypeInvalidException ex) { throw new FetchFailedException($"Fetch request failed: {ex.Message}"); } finally { if (uploadSession != null) { await AbortMultipartUpload(uploadSession); } } }
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); } }