1111using Microsoft . Extensions . Logging ;
1212using Microsoft . WindowsAzure . Storage ;
1313using Microsoft . WindowsAzure . Storage . Blob ;
14+ using Microsoft . WindowsAzure . Storage . DataMovement ;
1415
1516namespace NuGet . Services . Storage
1617{
1718 public class AzureStorage : Storage
1819 {
1920 private readonly ILogger < AzureStorage > _logger ;
2021 private readonly CloudBlobDirectory _directory ;
21-
22- public AzureStorage ( CloudStorageAccount account , string containerName , string path , Uri baseAddress , ILogger < AzureStorage > logger )
23- : this ( account . CreateCloudBlobClient ( ) . GetContainerReference ( containerName ) . GetDirectoryReference ( path ) , baseAddress , logger )
22+ private readonly bool _useServerSideCopy ;
23+
24+ public AzureStorage (
25+ CloudStorageAccount account ,
26+ string containerName ,
27+ string path ,
28+ Uri baseAddress ,
29+ bool useServerSideCopy ,
30+ bool initializeContainer ,
31+ ILogger < AzureStorage > logger )
32+ : this (
33+ account . CreateCloudBlobClient ( ) . GetContainerReference ( containerName ) . GetDirectoryReference ( path ) ,
34+ baseAddress ,
35+ useServerSideCopy ,
36+ initializeContainer ,
37+ logger )
2438 {
2539 }
2640
27- private AzureStorage ( CloudBlobDirectory directory , Uri baseAddress , ILogger < AzureStorage > logger )
41+ private AzureStorage (
42+ CloudBlobDirectory directory ,
43+ Uri baseAddress ,
44+ bool useServerSideCopy ,
45+ bool initializeContainer ,
46+ ILogger < AzureStorage > logger )
2847 : base ( baseAddress ?? GetDirectoryUri ( directory ) , logger )
2948 {
3049 _logger = logger ;
3150 _directory = directory ;
51+ _useServerSideCopy = useServerSideCopy ;
3252
33- if ( _directory . Container . CreateIfNotExists ( ) )
53+ if ( initializeContainer && _directory . Container . CreateIfNotExists ( ) )
3454 {
3555 _directory . Container . SetPermissions ( new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType . Blob } ) ;
3656
@@ -97,18 +117,88 @@ public override async Task<bool> ExistsAsync(string fileName, CancellationToken
97117 return false ;
98118 }
99119
100- public override async Task < IEnumerable < StorageListItem > > List ( CancellationToken cancellationToken )
120+ public override async Task < IEnumerable < StorageListItem > > List ( bool getMetadata , CancellationToken cancellationToken )
101121 {
102- var files = await _directory . ListBlobsAsync ( cancellationToken ) ;
122+ var files = await _directory . ListBlobsAsync ( getMetadata , cancellationToken ) ;
103123
104124 return files . Select ( GetStorageListItem ) . AsEnumerable ( ) ;
105125 }
106126
127+ public override async Task SetMetadataAsync ( Uri resourceUri , IDictionary < string , string > metadata )
128+ {
129+ var blob = GetBlockBlobReference ( GetName ( resourceUri ) ) ;
130+
131+ foreach ( var kvp in metadata )
132+ {
133+ blob . Metadata [ kvp . Key ] = kvp . Value ;
134+ }
135+
136+ await blob . SetMetadataAsync ( ) ;
137+ }
138+
107139 private StorageListItem GetStorageListItem ( IListBlobItem listBlobItem )
108140 {
109- var lastModified = ( listBlobItem as CloudBlockBlob ) ? . Properties . LastModified ? . UtcDateTime ;
141+ var cloudBlockBlob = ( listBlobItem as CloudBlockBlob ) ;
142+ var lastModified = cloudBlockBlob ? . Properties . LastModified ? . UtcDateTime ;
143+
144+ return new StorageListItem ( listBlobItem . Uri , lastModified , cloudBlockBlob ? . Metadata ) ;
145+ }
146+
147+ protected override async Task OnCopyAsync (
148+ Uri sourceUri ,
149+ IStorage destinationStorage ,
150+ Uri destinationUri ,
151+ IReadOnlyDictionary < string , string > destinationProperties ,
152+ CancellationToken cancellationToken )
153+ {
154+ var azureDestinationStorage = destinationStorage as AzureStorage ;
155+
156+ if ( azureDestinationStorage == null )
157+ {
158+ throw new NotImplementedException ( "Copying is only supported from Azure storage to Azure storage." ) ;
159+ }
160+
161+ string sourceName = GetName ( sourceUri ) ;
162+ string destinationName = azureDestinationStorage . GetName ( destinationUri ) ;
163+
164+ CloudBlockBlob sourceBlob = GetBlockBlobReference ( sourceName ) ;
165+ CloudBlockBlob destinationBlob = azureDestinationStorage . GetBlockBlobReference ( destinationName ) ;
166+
167+ var context = new SingleTransferContext ( ) ;
168+
169+ if ( destinationProperties ? . Count > 0 )
170+ {
171+ context . SetAttributesCallbackAsync = new SetAttributesCallbackAsync ( ( destination ) =>
172+ {
173+ var blob = ( CloudBlockBlob ) destination ;
174+
175+ // The copy statement copied all properties from the source blob to the destination blob; however,
176+ // there may be required properties on destination blob, all of which may have not already existed
177+ // on the source blob at the time of copy.
178+ foreach ( var property in destinationProperties )
179+ {
180+ switch ( property . Key )
181+ {
182+ case StorageConstants . CacheControl :
183+ blob . Properties . CacheControl = property . Value ;
184+ break ;
185+
186+ case StorageConstants . ContentType :
187+ blob . Properties . ContentType = property . Value ;
188+ break ;
189+
190+ default :
191+ throw new NotImplementedException ( $ "Storage property '{ property . Value } ' is not supported.") ;
192+ }
193+ }
194+
195+ return Task . CompletedTask ;
196+ } ) ;
197+ }
198+
199+ context . ShouldOverwriteCallbackAsync = new ShouldOverwriteCallbackAsync ( ( source , destination ) => Task . FromResult ( true ) ) ;
110200
111- return new StorageListItem ( listBlobItem . Uri , lastModified ) ;
201+ await TransferManager . CopyAsync ( sourceBlob , destinationBlob , _useServerSideCopy , options : null , context : context ) ;
112202 }
113203
114204 // save
@@ -143,7 +233,10 @@ await blob.UploadFromStreamAsync(
143233 operationContext : null ,
144234 cancellationToken : cancellationToken ) ;
145235
146- _logger . LogInformation ( "Saved compressed blob {BlobUri} to container {ContainerName}" , blob . Uri . ToString ( ) , _directory . Container . Name ) ;
236+ if ( Verbose )
237+ {
238+ _logger . LogInformation ( "Saved compressed blob {BlobUri} to container {ContainerName}" , blob . Uri . ToString ( ) , _directory . Container . Name ) ;
239+ }
147240 }
148241 }
149242 else
@@ -157,7 +250,10 @@ await blob.UploadFromStreamAsync(
157250 operationContext : null ,
158251 cancellationToken : cancellationToken ) ;
159252
160- _logger . LogInformation ( "Saved uncompressed blob {BlobUri} to container {ContainerName}" , blob . Uri . ToString ( ) , _directory . Container . Name ) ;
253+ if ( Verbose )
254+ {
255+ _logger . LogInformation ( "Saved uncompressed blob {BlobUri} to container {ContainerName}" , blob . Uri . ToString ( ) , _directory . Container . Name ) ;
256+ }
161257 }
162258 }
163259 }
@@ -217,5 +313,19 @@ protected override async Task OnDelete(Uri resourceUri, CancellationToken cancel
217313
218314 await blob . DeleteAsync ( cancellationToken ) ;
219315 }
316+
317+ private CloudBlockBlob GetBlockBlobReference ( string blobName )
318+ {
319+ var blob = _directory . GetBlockBlobReference ( blobName ) ;
320+
321+ ApplyBlobRequestOptions ( blob ) ;
322+
323+ return blob ;
324+ }
325+
326+ private void ApplyBlobRequestOptions ( CloudBlockBlob blob )
327+ {
328+ blob . ServiceClient . DefaultRequestOptions = _directory . ServiceClient . DefaultRequestOptions ;
329+ }
220330 }
221331}
0 commit comments