completeUpload method

JsonMap completeUpload(
  1. String principalId,
  2. String fileId,
  3. JsonMap body
)

completeUpload (12 3.4): checks the listed parts, joins them, checks size and SHA-256 and scans (or holds the scan, FakeFileSettings.holdScan); returns the File. A completed session answers the file again.

Throws 404, 410 gone, 409 conflict with upload_incomplete or part_conflict.

Implementation

JsonMap completeUpload(String principalId, String fileId, JsonMap body) {
  final f = _ownFile(principalId, fileId);
  _expire(f);
  if (f.sessionState == 'completed') return f.json;
  if (f.sessionState == 'aborted' || f.sessionState == 'expired') {
    throw const FakeProblem(410, 'gone');
  }
  for (final p in (body['parts'] as List<Object?>? ?? const [])) {
    final part = p! as JsonMap;
    final stored = f.parts[part['part_number']! as int];
    if (stored != null && stored.etag != part['etag']) {
      throw const FakeProblem(409, 'conflict', reason: 'part_conflict');
    }
  }
  final missing = [
    for (var n = 1; n <= f.partCount; n++)
      if (!f.parts.containsKey(n)) n,
  ];
  if (missing.isNotEmpty) {
    throw FakeProblem(
      409,
      'conflict',
      reason: 'upload_incomplete',
      extra: {'detail': 'missing parts ${missing.join(', ')}'},
    );
  }
  final joined = BytesBuilder(copy: false);
  for (var n = 1; n <= f.partCount; n++) {
    joined.add(f.parts[n]!.bytes);
  }
  final bytes = joined.takeBytes();
  f
    ..sessionState = 'completed'
    ..parts.clear()
    ..bytes = bytes;
  if (sha256.convert(bytes).toString() != f.declaredSha256) {
    f.json['upload_state'] = 'failed';
    _fileUpdated(f);
    return f.json;
  }
  f.json['mime_type'] = _detect(f.declaredMime, bytes);
  if (!fileSettings.holdScan) finishScan(fileId);
  return f.json;
}