8
8
using Flurl . Http ;
9
9
using System ;
10
10
using System . Collections . Generic ;
11
+ using System . IO ;
11
12
using System . Linq ;
12
13
using System . Linq . Expressions ;
13
14
using System . Net . Http ;
@@ -25,7 +26,7 @@ public class CouchDatabase<TSource> where TSource : CouchDocument
25
26
private readonly IFlurlClient _flurlClient ;
26
27
private readonly CouchSettings _settings ;
27
28
private readonly string _connectionString ;
28
- private string _database ;
29
+ private readonly string _database ;
29
30
30
31
/// <summary>
31
32
/// The database name.
@@ -228,10 +229,13 @@ public async Task<TSource> FindAsync(string docId, bool withConflicts = false)
228
229
request = request . SetQueryParam ( "conflicts" , true ) ;
229
230
}
230
231
231
- return await request
232
+ TSource document = await request
232
233
. GetJsonAsync < TSource > ( )
233
234
. SendRequestAsync ( )
234
235
. ConfigureAwait ( false ) ;
236
+
237
+ InitAttachments ( document ) ;
238
+ return document ;
235
239
}
236
240
catch ( CouchNotFoundException )
237
241
{
@@ -274,7 +278,14 @@ private async Task<List<TSource>> SendQueryAsync(Func<IFlurlRequest, Task<HttpRe
274
278
. SendRequestAsync ( )
275
279
. ConfigureAwait ( false ) ;
276
280
277
- return findResult . Docs . ToList ( ) ;
281
+ var documents = findResult . Docs . ToList ( ) ;
282
+
283
+ foreach ( TSource document in documents )
284
+ {
285
+ InitAttachments ( document ) ;
286
+ }
287
+
288
+ return documents ;
278
289
}
279
290
280
291
/// Finds all documents with given IDs.
@@ -291,8 +302,29 @@ public async Task<List<TSource>> FindManyAsync(IEnumerable<string> docIds)
291
302
} ) . ReceiveJson < BulkGetResult < TSource > > ( )
292
303
. SendRequestAsync ( )
293
304
. ConfigureAwait ( false ) ;
305
+
306
+ var documents = bulkGetResult . Results
307
+ . SelectMany ( r => r . Docs )
308
+ . Select ( d => d . Item )
309
+ . ToList ( ) ;
294
310
295
- return bulkGetResult . Results . SelectMany ( r => r . Docs ) . Select ( d => d . Item ) . ToList ( ) ;
311
+ foreach ( TSource document in documents )
312
+ {
313
+ InitAttachments ( document ) ;
314
+ }
315
+
316
+ return documents ;
317
+ }
318
+
319
+ private void InitAttachments ( TSource document )
320
+ {
321
+ foreach ( CouchAttachment attachment in document . Attachments )
322
+ {
323
+ attachment . DocumentId = document . Id ;
324
+ attachment . DocumentRev = document . Rev ;
325
+ var path = $ "{ _connectionString } /{ _database } /{ document . Id } /{ Uri . EscapeUriString ( attachment . Name ) } ";
326
+ attachment . Uri = new Uri ( path ) ;
327
+ }
296
328
}
297
329
298
330
#endregion
@@ -325,8 +357,11 @@ public async Task<TSource> CreateAsync(TSource document, bool batch = false)
325
357
. ReceiveJson < DocumentSaveResponse > ( )
326
358
. SendRequestAsync ( )
327
359
. ConfigureAwait ( false ) ;
328
-
329
360
document . ProcessSaveResponse ( response ) ;
361
+
362
+ await UpdateAttachments ( document )
363
+ . ConfigureAwait ( false ) ;
364
+
330
365
return document ;
331
366
}
332
367
@@ -356,8 +391,11 @@ public async Task<TSource> CreateOrUpdateAsync(TSource document, bool batch = fa
356
391
. ReceiveJson < DocumentSaveResponse > ( )
357
392
. SendRequestAsync ( )
358
393
. ConfigureAwait ( false ) ;
359
-
360
394
document . ProcessSaveResponse ( response ) ;
395
+
396
+ await UpdateAttachments ( document )
397
+ . ConfigureAwait ( false ) ;
398
+
361
399
return document ;
362
400
}
363
401
@@ -410,6 +448,9 @@ public async Task<IEnumerable<TSource>> CreateOrUpdateRangeAsync(IEnumerable<TSo
410
448
foreach ( ( TSource document , DocumentSaveResponse saveResponse ) in zipped )
411
449
{
412
450
document . ProcessSaveResponse ( saveResponse ) ;
451
+
452
+ await UpdateAttachments ( document )
453
+ . ConfigureAwait ( false ) ;
413
454
}
414
455
415
456
return documents ;
@@ -435,10 +476,76 @@ public async Task EnsureFullCommitAsync()
435
476
}
436
477
}
437
478
479
+ private async Task UpdateAttachments ( TSource document )
480
+ {
481
+ foreach ( CouchAttachment attachment in document . Attachments . GetAddedAttachments ( ) )
482
+ {
483
+ var stream = new StreamContent (
484
+ new FileStream ( attachment . FileInfo . FullName , FileMode . Open ) ) ;
485
+
486
+ AttachmentResult response = await NewRequest ( )
487
+ . AppendPathSegment ( document . Id )
488
+ . AppendPathSegment ( Uri . EscapeUriString ( attachment . Name ) )
489
+ . WithHeader ( "Content-Type" , attachment . ContentType )
490
+ . WithHeader ( "If-Match" , document . Rev )
491
+ . PutAsync ( stream )
492
+ . ReceiveJson < AttachmentResult > ( )
493
+ . ConfigureAwait ( false ) ;
494
+
495
+ if ( response . Ok )
496
+ {
497
+ document . Rev = response . Rev ;
498
+ attachment . FileInfo = null ;
499
+ }
500
+ }
501
+
502
+ foreach ( CouchAttachment attachment in document . Attachments . GetDeletedAttachments ( ) )
503
+ {
504
+ AttachmentResult response = await NewRequest ( )
505
+ . AppendPathSegment ( document . Id )
506
+ . AppendPathSegment ( attachment . Name )
507
+ . WithHeader ( "If-Match" , document . Rev )
508
+ . DeleteAsync ( )
509
+ . ReceiveJson < AttachmentResult > ( )
510
+ . ConfigureAwait ( false ) ;
511
+
512
+ if ( response . Ok )
513
+ {
514
+ document . Rev = response . Rev ;
515
+ document . Attachments . RemoveAttachment ( attachment ) ;
516
+ }
517
+ }
518
+
519
+ InitAttachments ( document ) ;
520
+ }
521
+
438
522
#endregion
439
523
440
524
#region Utils
441
525
526
+ /// <summary>
527
+ /// Asynchronously downloads a specific attachment.
528
+ /// </summary>
529
+ /// <param name="attachment">The attachment to download.</param>
530
+ /// <param name="localFolderPath">Path of local folder where file is to be downloaded.</param>
531
+ /// <param name="localFileName">Name of local file. If not specified, the source filename (from Content-Dispostion header, or last segment of the URL) is used.</param>
532
+ /// <param name="bufferSize">Buffer size in bytes. Default is 4096.</param>
533
+ /// <returns>The path of the downloaded file.</returns>
534
+ public async Task < string > DownloadAttachment ( CouchAttachment attachment , string localFolderPath , string localFileName = null , int bufferSize = 4096 )
535
+ {
536
+ if ( attachment . Uri == null )
537
+ {
538
+ throw new InvalidOperationException ( "The attachment is not uploaded yet." ) ;
539
+ }
540
+
541
+ return await NewRequest ( )
542
+ . AppendPathSegment ( attachment . DocumentId )
543
+ . AppendPathSegment ( Uri . EscapeUriString ( attachment . Name ) )
544
+ . WithHeader ( "If-Match" , attachment . DocumentRev )
545
+ . DownloadFileAsync ( localFolderPath , localFileName , bufferSize )
546
+ . ConfigureAwait ( false ) ;
547
+ }
548
+
442
549
/// <summary>
443
550
/// Requests compaction of the specified database.
444
551
/// </summary>
0 commit comments