11using Newtonsoft . Json . Converters ;
22using System . Diagnostics ;
33using System . Net . Http . Headers ;
4- using System . Reflection ;
54using System . Text ;
65using System . Text . RegularExpressions ;
76
@@ -312,29 +311,61 @@ public async Task<AttachmentUploadResponse> UploadAttachment(FileInfo file, stri
312311 if ( ! file . Exists )
313312 throw new FileNotFoundException ( file . FullName ) ;
314313
314+ using ( FileStream stream = file . OpenRead ( ) )
315+ return await UploadAttachment ( stream as Stream , file . Name , contentType ) ;
316+ }
317+
318+ /// <summary>
319+ /// Upload a file to the 4me AWS S3 storage.
320+ /// </summary>
321+ /// <param name="stream">The content to upload. The stream needs to support seeking in order to determine the HTTP Content-Length header.</param>
322+ /// <param name="fileName">The file name.</param>
323+ /// <param name="contentType">The content type of the file.</param>
324+ /// <returns>A <see cref="AttachmentUploadResponse"/> containing the 4me AWS S3 file storage reference key and file size.</returns>
325+ /// <exception cref="FileNotFoundException"></exception>
326+ /// <exception cref="Sdk4meException"></exception>
327+ public async Task < AttachmentUploadResponse > UploadAttachment ( StreamReader stream , string fileName , string contentType )
328+ {
329+ return await UploadAttachment ( stream . BaseStream , fileName , contentType ) ;
330+ }
331+
332+ /// <summary>
333+ /// Upload a file to the 4me AWS S3 storage.
334+ /// </summary>
335+ /// <param name="stream">The content to upload. The stream needs to support seeking in order to determine the HTTP Content-Length header.</param>
336+ /// <param name="fileName">The file name.</param>
337+ /// <param name="contentType">The content type of the file.</param>
338+ /// <returns>A <see cref="AttachmentUploadResponse"/> containing the 4me AWS S3 file storage reference key and file size.</returns>
339+ /// <exception cref="Sdk4meException"></exception>
340+ public async Task < AttachmentUploadResponse > UploadAttachment ( Stream stream , string fileName , string contentType )
341+ {
342+ if ( ! stream . CanSeek )
343+ throw new Sdk4meException ( "The stream needs to support seeking in order to determine the HTTP Content-Length header." ) ;
344+
315345 DataList < AttachmentStorage > attachmentStorages = await Get < AttachmentStorage > ( Query . AttachmentStorage ) ;
316346 if ( attachmentStorages . FirstOrDefault ( ) is AttachmentStorage attachmentStorage )
317347 {
318- if ( attachmentStorage . AllowedExtensions != null && attachmentStorage . AllowedExtensions . Contains ( file . Extension . TrimStart ( '.' ) ) )
348+ string fileExtension = Path . GetExtension ( fileName ) . TrimStart ( '.' ) ;
349+ if ( attachmentStorage . AllowedExtensions != null && attachmentStorage . AllowedExtensions . Contains ( fileExtension , StringComparer . InvariantCultureIgnoreCase ) )
319350 {
320- if ( file . Length >= attachmentStorage . SizeLimit )
351+ if ( stream . Length >= attachmentStorage . SizeLimit )
321352 throw new Sdk4meException ( $ "File size exceeded, the maximum size is { attachmentStorage . SizeLimit } byte") ;
322353
323354 Dictionary < string , string > storageFacility = attachmentStorage ? . ProviderParameters ? . ToObject < Dictionary < string , string > > ( ) ?? throw new Sdk4meException ( "File upload failed, invalid AttachmentStorage.ProviderParameters value." ) ;
324355 MultipartFormDataContent multipartContent = new ( )
325- {
326- { new StringContent ( contentType ) , "Content-Type" } ,
327- { new StringContent ( storageFacility [ "acl" ] ) , "acl" } ,
328- { new StringContent ( storageFacility [ "key" ] ) , "key" } ,
329- { new StringContent ( storageFacility [ "policy" ] ) , "policy" } ,
330- { new StringContent ( storageFacility [ "success_action_status" ] ) , "success_action_status" } ,
331- { new StringContent ( storageFacility [ "x-amz-algorithm" ] ) , "x-amz-algorithm" } ,
332- { new StringContent ( storageFacility [ "x-amz-credential" ] ) , "x-amz-credential" } ,
333- { new StringContent ( storageFacility [ "x-amz-date" ] ) , "x-amz-date" } ,
334- { new StringContent ( storageFacility [ "x-amz-server-side-encryption" ] ) , "x-amz-server-side-encryption" } ,
335- { new StringContent ( storageFacility [ "x-amz-signature" ] ) , "x-amz-signature" } ,
336- { new ByteArrayContent ( File . ReadAllBytes ( file . FullName ) ) , "file" , file . Name }
337- } ;
356+ {
357+ { new StringContent ( contentType ) , "Content-Type" } ,
358+ { new StringContent ( storageFacility [ "acl" ] ) , "acl" } ,
359+ { new StringContent ( storageFacility [ "key" ] ) , "key" } ,
360+ { new StringContent ( storageFacility [ "policy" ] ) , "policy" } ,
361+ { new StringContent ( storageFacility [ "success_action_status" ] ) , "success_action_status" } ,
362+ { new StringContent ( storageFacility [ "x-amz-algorithm" ] ) , "x-amz-algorithm" } ,
363+ { new StringContent ( storageFacility [ "x-amz-credential" ] ) , "x-amz-credential" } ,
364+ { new StringContent ( storageFacility [ "x-amz-date" ] ) , "x-amz-date" } ,
365+ { new StringContent ( storageFacility [ "x-amz-server-side-encryption" ] ) , "x-amz-server-side-encryption" } ,
366+ { new StringContent ( storageFacility [ "x-amz-signature" ] ) , "x-amz-signature" } ,
367+ { new StreamContent ( stream ) , "file" , fileName }
368+ } ;
338369
339370 using ( HttpRequestMessage requestMessage = new ( HttpMethod . Post , attachmentStorage . UploadUri ) { Content = multipartContent } )
340371 {
@@ -351,7 +382,7 @@ public async Task<AttachmentUploadResponse> UploadAttachment(FileInfo file, stri
351382 return new ( )
352383 {
353384 Key = match . Value ,
354- Size = file . Length
385+ Size = stream . Length
355386 } ;
356387 }
357388 else
@@ -362,7 +393,10 @@ public async Task<AttachmentUploadResponse> UploadAttachment(FileInfo file, stri
362393 }
363394 }
364395 }
365- throw new Sdk4meException ( $ "The { file . Extension } extension is not allowed on this 4me instance.") ;
396+ if ( fileExtension == string . Empty )
397+ throw new Sdk4meException ( $ "A file name without extension is not allowed on this 4me instance.") ;
398+ else
399+ throw new Sdk4meException ( $ "The '{ fileExtension } ' extension is not allowed on this 4me instance.") ;
366400 }
367401 throw new Sdk4meException ( "No AttachmentStorage object returned by the GraphQL API." ) ;
368402 }
0 commit comments