@@ -38,25 +38,21 @@ abstract class DevFSContent {
38
38
/// or if the given time is null.
39
39
bool isModifiedAfter (DateTime time);
40
40
41
- /// The number of bytes in this file.
42
41
int get size;
43
42
44
- /// Returns the raw bytes of this file.
45
- List <int > contentsAsBytes ();
43
+ Future <List <int >> contentsAsBytes ();
46
44
47
- /// Returns a gzipped representation of the contents of this file.
48
- List <int > contentsAsCompressedBytes () {
49
- return gzip.encode (contentsAsBytes ());
45
+ Stream <List <int >> contentsAsStream ();
46
+
47
+ Stream <List <int >> contentsAsCompressedStream () {
48
+ return contentsAsStream ().cast <List <int >>().transform <List <int >>(gzip.encoder);
50
49
}
51
50
52
- /// Copies the content into the provided file.
53
- ///
54
- /// Requires that the `destination` directory already exists, but the target
55
- /// file need not.
56
- void copyToFile (File destination);
51
+ /// Return the list of files this content depends on.
52
+ List <String > get fileDependencies => < String > [];
57
53
}
58
54
59
- /// File content to be copied to the device.
55
+ // File content to be copied to the device.
60
56
class DevFSFileContent extends DevFSContent {
61
57
DevFSFileContent (this .file);
62
58
@@ -106,6 +102,9 @@ class DevFSFileContent extends DevFSContent {
106
102
}
107
103
}
108
104
105
+ @override
106
+ List <String > get fileDependencies => < String > [_getFile ().path];
107
+
109
108
@override
110
109
bool get isModified {
111
110
final FileStat _oldFileStat = _fileStat;
@@ -136,12 +135,10 @@ class DevFSFileContent extends DevFSContent {
136
135
}
137
136
138
137
@override
139
- List <int > contentsAsBytes () => _getFile ().readAsBytesSync (). cast < int > ();
138
+ Future < List <int >> contentsAsBytes () => _getFile ().readAsBytes ();
140
139
141
140
@override
142
- void copyToFile (File destination) {
143
- _getFile ().copySync (destination.path);
144
- }
141
+ Stream <List <int >> contentsAsStream () => _getFile ().openRead ();
145
142
}
146
143
147
144
/// Byte content to be copied to the device.
@@ -178,15 +175,14 @@ class DevFSByteContent extends DevFSContent {
178
175
int get size => _bytes.length;
179
176
180
177
@override
181
- List <int > contentsAsBytes () => _bytes;
178
+ Future < List <int >> contentsAsBytes () async => _bytes;
182
179
183
180
@override
184
- void copyToFile (File destination) {
185
- destination.writeAsBytesSync (contentsAsBytes ());
186
- }
181
+ Stream <List <int >> contentsAsStream () =>
182
+ Stream <List <int >>.fromIterable (< List <int >> [_bytes]);
187
183
}
188
184
189
- /// String content to be copied to the device encoded as utf8 .
185
+ /// String content to be copied to the device.
190
186
class DevFSStringContent extends DevFSByteContent {
191
187
DevFSStringContent (String string)
192
188
: _string = string,
@@ -207,30 +203,76 @@ class DevFSStringContent extends DevFSByteContent {
207
203
}
208
204
}
209
205
210
- class DevFSOperations {
211
- DevFSOperations (this .vmService, this .fsName)
212
- : httpAddress = vmService.httpAddress;
213
-
214
- final VMService vmService;
215
- final String fsName;
216
- final Uri httpAddress;
217
- final HttpClient _client = HttpClient ();
206
+ /// Abstract DevFS operations interface.
207
+ abstract class DevFSOperations {
208
+ Future <Uri > create (String fsName);
209
+ Future <dynamic > destroy (String fsName);
210
+ Future <dynamic > writeFile (String fsName, Uri deviceUri, DevFSContent content);
211
+ }
218
212
219
- static const int kMaxInFlight = 6 ;
213
+ /// An implementation of [DevFSOperations] that speaks to the
214
+ /// vm service.
215
+ class ServiceProtocolDevFSOperations implements DevFSOperations {
216
+ ServiceProtocolDevFSOperations (this .vmService);
220
217
221
- int _inFlight = 0 ;
222
- Map <Uri , DevFSContent > _outstanding;
223
- Completer <void > _completer;
218
+ final VMService vmService;
224
219
220
+ @override
225
221
Future <Uri > create (String fsName) async {
226
222
final Map <String , dynamic > response = await vmService.vm.createDevFS (fsName);
227
223
return Uri .parse (response['uri' ]);
228
224
}
229
225
230
- Future <void > destroy (String fsName) async {
226
+ @override
227
+ Future <dynamic > destroy (String fsName) async {
231
228
await vmService.vm.deleteDevFS (fsName);
232
229
}
233
230
231
+ @override
232
+ Future <dynamic > writeFile (String fsName, Uri deviceUri, DevFSContent content) async {
233
+ List <int > bytes;
234
+ try {
235
+ bytes = await content.contentsAsBytes ();
236
+ } catch (e) {
237
+ return e;
238
+ }
239
+ final String fileContents = base64.encode (bytes);
240
+ try {
241
+ return await vmService.vm.invokeRpcRaw (
242
+ '_writeDevFSFile' ,
243
+ params: < String , dynamic > {
244
+ 'fsName' : fsName,
245
+ 'uri' : deviceUri.toString (),
246
+ 'fileContents' : fileContents,
247
+ },
248
+ );
249
+ } catch (error) {
250
+ printTrace ('DevFS: Failed to write $deviceUri : $error ' );
251
+ }
252
+ }
253
+ }
254
+
255
+ class DevFSException implements Exception {
256
+ DevFSException (this .message, [this .error, this .stackTrace]);
257
+ final String message;
258
+ final dynamic error;
259
+ final StackTrace stackTrace;
260
+ }
261
+
262
+ class _DevFSHttpWriter {
263
+ _DevFSHttpWriter (this .fsName, VMService serviceProtocol)
264
+ : httpAddress = serviceProtocol.httpAddress;
265
+
266
+ final String fsName;
267
+ final Uri httpAddress;
268
+
269
+ static const int kMaxInFlight = 6 ;
270
+
271
+ int _inFlight = 0 ;
272
+ Map <Uri , DevFSContent > _outstanding;
273
+ Completer <void > _completer;
274
+ final HttpClient _client = HttpClient ();
275
+
234
276
Future <void > write (Map <Uri , DevFSContent > entries) async {
235
277
_client.maxConnectionsPerHost = kMaxInFlight;
236
278
_completer = Completer <void >();
@@ -259,9 +301,9 @@ class DevFSOperations {
259
301
final HttpClientRequest request = await _client.putUrl (httpAddress);
260
302
request.headers.removeAll (HttpHeaders .acceptEncodingHeader);
261
303
request.headers.add ('dev_fs_name' , fsName);
262
- request.headers.add ('dev_fs_uri_b64' ,
263
- base64. encode (utf8. encode (deviceUri. toString ())) );
264
- request.add (content. contentsAsCompressedBytes () );
304
+ request.headers.add ('dev_fs_uri_b64' , base64. encode (utf8. encode ( '$ deviceUri ' )));
305
+ final Stream < List < int >> contents = content. contentsAsCompressedStream ( );
306
+ await request.addStream (contents );
265
307
final HttpClientResponse response = await request.close ();
266
308
await response.drain <void >();
267
309
} catch (error, trace) {
@@ -275,14 +317,6 @@ class DevFSOperations {
275
317
}
276
318
}
277
319
278
- class DevFSException implements Exception {
279
- DevFSException (this .message, [this .error, this .stackTrace]);
280
-
281
- final String message;
282
- final dynamic error;
283
- final StackTrace stackTrace;
284
- }
285
-
286
320
// Basic statistics for DevFS update operation.
287
321
class UpdateFSReport {
288
322
UpdateFSReport ({
@@ -319,17 +353,20 @@ class DevFS {
319
353
this .fsName,
320
354
this .rootDirectory, {
321
355
String packagesFilePath,
322
- }) : _operations = DevFSOperations (serviceProtocol, fsName),
356
+ }) : _operations = ServiceProtocolDevFSOperations (serviceProtocol),
357
+ _httpWriter = _DevFSHttpWriter (fsName, serviceProtocol),
323
358
_packagesFilePath = packagesFilePath ?? fs.path.join (rootDirectory.path, kPackagesFileName);
324
359
325
360
DevFS .operations (
326
361
this ._operations,
327
362
this .fsName,
328
363
this .rootDirectory, {
329
364
String packagesFilePath,
330
- }) : _packagesFilePath = packagesFilePath ?? fs.path.join (rootDirectory.path, kPackagesFileName);
365
+ }) : _httpWriter = null ,
366
+ _packagesFilePath = packagesFilePath ?? fs.path.join (rootDirectory.path, kPackagesFileName);
331
367
332
368
final DevFSOperations _operations;
369
+ final _DevFSHttpWriter _httpWriter;
333
370
final String fsName;
334
371
final Directory rootDirectory;
335
372
String _packagesFilePath;
@@ -452,7 +489,7 @@ class DevFS {
452
489
printTrace ('Updating files' );
453
490
if (dirtyEntries.isNotEmpty) {
454
491
try {
455
- await _operations .write (dirtyEntries);
492
+ await _httpWriter .write (dirtyEntries);
456
493
} on SocketException catch (socketException, stackTrace) {
457
494
printTrace ('DevFS sync failed. Lost connection to device: $socketException ' );
458
495
throw DevFSException ('Lost connection to device.' , socketException, stackTrace);
0 commit comments