@@ -2,6 +2,7 @@ import 'dart:async';
22import 'dart:convert' ;
33
44import 'package:_discoveryapis_commons/_discoveryapis_commons.dart' ;
5+ import 'package:clock/clock.dart' ;
56import 'package:crypto/crypto.dart' ;
67import 'package:gcloud/storage.dart' ;
78import 'package:logging/logging.dart' ;
@@ -67,9 +68,13 @@ class MemStorage implements Storage {
6768 _logger.info ('Copy object from $src to $dest ' );
6869 final srcUri = Uri .parse (src);
6970 final destUri = Uri .parse (dest);
70- await bucket (srcUri.host)
71- .read (srcUri.path.substring (1 ))
72- .pipe (bucket (destUri.host).write (destUri.path.substring (1 )));
71+ final srcBucket = _buckets[srcUri.host]! ;
72+ final srcObject = srcBucket._files[srcUri.path.substring (1 )]! ;
73+ await bucket (destUri.host).writeBytes (
74+ destUri.path.substring (1 ),
75+ srcObject.content,
76+ metadata: metadata ?? srcObject.metadata,
77+ );
7378 }
7479
7580 /// Serializes the content of the Storage to the [sink] , with a line-by-line
@@ -107,18 +112,38 @@ class MemStorage implements Storage {
107112 'name' : file.name,
108113 'content' : base64.encode (file.content),
109114 'updated' : file.updated.toUtc ().toIso8601String (),
110- 'metadata' : null , // TODO: add metadata support
115+ 'metadata' : {
116+ 'contentType' : file.metadata.contentType,
117+ 'contentEncoding' : file.metadata.contentEncoding,
118+ 'cacheControl' : file.metadata.cacheControl,
119+ 'contentDisposition' : file.metadata.contentDisposition,
120+ 'contentLanguage' : file.metadata.contentLanguage,
121+ 'custom' : file.metadata.custom,
122+ },
111123 };
112124 }
113125
114126 _File _decodeFile (Map <String , dynamic > map) {
115127 final content = base64.decode (map['content' ] as String );
116128 final updated = DateTime .parse (map['updated' ] as String );
129+ final meta = map['metadata' ] ?? < String , Object ? > {};
117130 return _File (
118131 bucketName: map['bucket' ] as String ,
119132 name: map['name' ] as String ,
120133 content: content,
121134 updated: updated,
135+ metadata: ObjectMetadata (
136+ acl: Acl ([]),
137+ contentType: meta['contentType' ] as String ? ,
138+ contentEncoding: meta['contentEncoding' ] as String ? ,
139+ cacheControl: meta['cacheControl' ] as String ? ,
140+ contentDisposition: meta['contentDisposition' ] as String ? ,
141+ contentLanguage: meta['contentLanguage' ] as String ? ,
142+ custom: (meta['custom' ] as Map ? )? .map (
143+ (k, v) => MapEntry (k as String , v as String ),
144+ ) ??
145+ < String , String > {},
146+ ),
122147 );
123148 }
124149}
@@ -141,12 +166,12 @@ class _File implements BucketObjectEntry {
141166 required this .bucketName,
142167 required this .name,
143168 required this .content,
169+ required this .metadata,
144170 DateTime ? updated,
145171 }) : // TODO: use a real CRC32 check
146172 crc32CChecksum = content.fold <int >(0 , (a, b) => a + b) & 0xffffffff ,
147173 md5Hash = md5.convert (content).bytes,
148- updated = updated ?? DateTime .now ().toUtc (),
149- metadata = ObjectMetadata (acl: Acl ([]));
174+ updated = updated ?? clock.now ().toUtc ();
150175
151176 @override
152177 Uri get downloadLink => Uri (scheme: 'gs' , host: bucketName, path: name);
@@ -199,10 +224,18 @@ class _Bucket implements Bucket {
199224 buffer.addAll (data);
200225 return buffer;
201226 }).then ((content) {
227+ var meta = metadata ?? ObjectMetadata ();
228+ if (acl != null ) {
229+ meta = meta.replace (acl: acl);
230+ }
231+ if (contentType != null ) {
232+ meta = meta.replace (contentType: contentType);
233+ }
202234 _files[objectName] = _File (
203235 bucketName: bucketName,
204236 name: objectName,
205237 content: content,
238+ metadata: meta,
206239 );
207240 _logger.info ('Completed ${content .length } bytes: $objectName ' );
208241 });
0 commit comments