|
2 | 2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | 3 | // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
|
5 | | -import 'dart:typed_data'; |
| 5 | +import 'dart:io'; |
6 | 6 |
|
7 | 7 | import 'package:crypto/crypto.dart'; |
8 | 8 | import 'package:gcloud/storage.dart'; |
@@ -102,26 +102,43 @@ class TarballStorage { |
102 | 102 | Future<ContentMatchStatus> matchArchiveContentInCanonical( |
103 | 103 | String package, |
104 | 104 | String version, |
105 | | - Uint8List bytes, |
| 105 | + File file, |
106 | 106 | ) async { |
107 | 107 | final objectName = tarballObjectName(package, version); |
108 | 108 | final info = await _canonicalBucket.tryInfo(objectName); |
109 | 109 | if (info == null) { |
110 | 110 | return ContentMatchStatus.missing; |
111 | 111 | } |
112 | | - if (info.length != bytes.length) { |
| 112 | + if (info.length != await file.length()) { |
113 | 113 | return ContentMatchStatus.different; |
114 | 114 | } |
115 | | - final md5hash = md5.convert(bytes).bytes; |
| 115 | + final md5hash = (await file.openRead().transform(md5).single).bytes; |
116 | 116 | if (!md5hash.byteToByteEquals(info.md5Hash)) { |
117 | 117 | return ContentMatchStatus.different; |
118 | 118 | } |
119 | | - final objectBytes = await _canonicalBucket.readAsBytes(objectName); |
120 | | - if (bytes.byteToByteEquals(objectBytes)) { |
121 | | - return ContentMatchStatus.same; |
122 | | - } else { |
| 119 | + // Limit memory use while doing the byte-to-byte comparison by streaming it chunk-wise. |
| 120 | + final raf = await file.open(); |
| 121 | + var remainingLength = info.length; |
| 122 | + try { |
| 123 | + await for (final chunk in _canonicalBucket.read(objectName)) { |
| 124 | + if (chunk.isEmpty) continue; |
| 125 | + remainingLength -= chunk.length; |
| 126 | + if (remainingLength < 0) { |
| 127 | + return ContentMatchStatus.different; |
| 128 | + } |
| 129 | + // TODO: consider rewriting to fixed-length chunk comparison |
| 130 | + final fileChunk = await raf.read(chunk.length); |
| 131 | + if (!fileChunk.byteToByteEquals(chunk)) { |
| 132 | + return ContentMatchStatus.different; |
| 133 | + } |
| 134 | + } |
| 135 | + } finally { |
| 136 | + await raf.close(); |
| 137 | + } |
| 138 | + if (remainingLength != 0) { |
123 | 139 | return ContentMatchStatus.different; |
124 | 140 | } |
| 141 | + return ContentMatchStatus.same; |
125 | 142 | } |
126 | 143 |
|
127 | 144 | /// Copies the uploaded object from the temp bucket to the canonical bucket. |
|
0 commit comments