deleteLargeFile function

Future<void> deleteLargeFile({
  1. required String remoteFilePath,
  2. String? ownerWebId,
  3. bool isPodRelativePath = false,
  4. void onProgress(
    1. int,
    2. int
    )?,
})

Delete a large file previously sent using writeLargeFile with URL remoteFilePath (relative to appname/data directory) in POD.

By default the file is deleted from the current user's own POD. To delete a large file owned by another user, provide that owner's WebID via ownerWebId (consistent with writeLargeFile and readLargeFile).

Implementation

Future<void> deleteLargeFile({
  required String remoteFilePath,
  String? ownerWebId,
  bool isPodRelativePath = false,
  void Function(int, int)? onProgress,
}) async {
  // Check if the corresponding Turtle file and directory of chunks exist

  final externWebId = await resolveExternalOwner(ownerWebId);

  final filePath = isPodRelativePath
      ? remoteFilePath
      : [await getDataDirPath(), remoteFilePath].join('/');
  final chunkDirUrl = await getDirUrl(
    _getChunkDirPath(filePath),
    webId: externWebId,
  );
  final fileUrl = await getFileUrl('$filePath.ttl', webId: externWebId);

  if (await checkResourceStatus(fileUrl, isFile: true) !=
          ResourceStatus.exist &&
      await checkResourceStatus(chunkDirUrl, isFile: false) !=
          ResourceStatus.exist) {
    debugPrint('The requested file does not exist.');
    return;
  }

  // For a large file on the user's OWN POD, revoke any recipients' access to
  // the file's shareable resources (the metadata file and the chunk directory)
  // before deleting them, so the recipients' permission logs no longer show
  // access to a file that no longer exists. This mirrors deleteFile() for
  // regular files. Revocation rewrites ACLs and is therefore only possible on
  // the user's own POD, so it is skipped for external deletions.

  if (externWebId == null) {
    await _revokeLargeFileRecipients(fileUrl, isFile: true);
    await _revokeLargeFileRecipients(chunkDirUrl, isFile: false);
  }

  // Parse the Turtle file with metadata of the (chunked) large file
  // on server to get the URLs of individual chunks

  final triples = turtleToTripleMap(
    externWebId == null
        ? await readPod(fileUrl, pathType: PathType.absoluteUrl)
        : await readExternalPod(fileUrl),
  );
  assert(triples.length == 1);
  assert(triples.containsKey(fileUrl));

  final map = triples[fileUrl];
  final chunkPred = SIIPredicate.dataChunk.uriRef.value;
  assert(map!.containsKey(chunkPred));

  // Delete the individual chunks.
  //
  // turtleToTripleMap() yields a bare String when a predicate has a single
  // object and an Iterable when it has several, so a file small enough to fit
  // in one chunk arrives here as a String. Normalise as the read path does
  // (see getChunkStream below); iterating the String directly threw
  // "type 'String' is not a subtype of type 'Iterable<dynamic>'".

  final chunkObj = map![chunkPred];
  assert(chunkObj != null);
  final chunkUrls = chunkObj is Iterable
      ? chunkObj.map((e) => e as String).toList()
      : [chunkObj as String];
  final chunkCount = chunkUrls.length;
  var deleted = 0;

  for (final chunkUrl in chunkUrls) {
    await deleteResource(chunkUrl, ResourceContentType.binary);
    // await deleteAclForResource(chunkUrl);  // this may not be necessary

    deleted += 1;

    if (onProgress != null) {
      onProgress(deleted, chunkCount);
    }
  }

  // Delete the directory with individual chunks
  await deleteResource(
    '$chunkDirUrl${_getChunkDirInitFileName()}',
    ResourceContentType.turtleText,
  );
  await deleteAclForResource(chunkDirUrl);
  await deleteResource(chunkDirUrl, ResourceContentType.directory);

  // Delete the representing turtle file

  await deleteResource(fileUrl, ResourceContentType.turtleText);

  // A successful delete is not announced, matching deleteFile() for ordinary
  // files, which logs only warnings. Callers that replace a large file delete
  // then write, so announcing it printed a "Deleted" line per file for what
  // was really an overwrite.
}