extractFileToDisk function

Future<void> extractFileToDisk(
  1. String inputPath,
  2. String outputPath, {
  3. String? password,
  4. int? bufferSize,
  5. ArchiveCallback? callback,
})

Implementation

Future<void> extractFileToDisk(String inputPath, String outputPath,
    {String? password, int? bufferSize, ArchiveCallback? callback}) async {
  Directory? tempDir;
  var archivePath = inputPath;

  var posixSupported = posix.isPosixSupported();

  const String extensionMsg =
      '.tar.gz, .tgz, .tar.bz2, .tbz, .tar.xz, .txz, .tar or .zip';

  // get the extension of the input file with up to 2 components
  // e.g. for file.tar.gz, it will return '.tar.gz'
  var archiveExt = getInputExtension(archivePath);
  if (archiveExt.isEmpty) {
    throw ArgumentError.value(
      inputPath,
      'inputPath',
      'No file extension detected, must end with $extensionMsg',
    );
  }

  if (archiveExt == '.tar.gz' || archiveExt == '.tgz') {
    tempDir = Directory.systemTemp.createTempSync('dart_archive');
    archivePath = path.join(tempDir.path, 'temp.tar');
    final input = InputFileStream(inputPath);
    final output = OutputFileStream(archivePath, bufferSize: bufferSize);
    GZipDecoder().decodeStream(input, output);
    await input.close();
    await output.close();
    archiveExt = '.tar';
  } else if (archiveExt == '.tar.bz2' || archiveExt == '.tbz') {
    tempDir = Directory.systemTemp.createTempSync('dart_archive');
    archivePath = path.join(tempDir.path, 'temp.tar');
    final input = InputFileStream(inputPath);
    final output = OutputFileStream(archivePath, bufferSize: bufferSize);
    BZip2Decoder().decodeStream(input, output);
    await input.close();
    await output.close();
    archiveExt = '.tar';
  } else if (archiveExt == '.tar.xz' || archiveExt == '.txz') {
    tempDir = Directory.systemTemp.createTempSync('dart_archive');
    archivePath = path.join(tempDir.path, 'temp.tar');
    final input = InputFileStream(inputPath);
    final output = OutputFileStream(archivePath, bufferSize: bufferSize);
    XZDecoder().decodeStream(input, output);
    await input.close();
    await output.close();
    archiveExt = '.tar';
  }

  InputStream? toClose;

  Archive archive;
  if (archiveExt == '.tar') {
    final input = InputFileStream(archivePath);
    archive = TarDecoder().decodeStream(input, callback: callback);
    toClose = input;
  } else if (archiveExt == '.zip') {
    final input = InputFileStream(archivePath);
    archive = ZipDecoder()
        .decodeStream(input, password: password, callback: callback);
    toClose = input;
  } else {
    throw ArgumentError.value(inputPath, 'inputPath', 'Must end $extensionMsg');
  }

  for (final file in archive) {
    final filePath = path.join(outputPath, path.normalize(file.name));
    if (!_isWithinOutputPath(outputPath, filePath)) {
      continue;
    }

    if (file.isSymbolicLink) {
      if (!_isValidSymLink(outputPath, file)) {
        continue;
      }
    }

    if (file.isDirectory && !file.isSymbolicLink) {
      Directory(filePath).createSync(recursive: true);
      continue;
    }

    if (file.isSymbolicLink) {
      final link = Link(filePath);
      final p = path.normalize(file.symbolicLink ?? "");
      link.createSync(p, recursive: true);
    } else if (file.isFile) {
      final output = OutputFileStream(filePath, bufferSize: bufferSize);
      try {
        file.writeContent(output);
      } catch (_) {}
      if (posixSupported) {
        posix.chmod(filePath, file.unixPermissions.toRadixString(8));
      }

      await output.close();
    }
  }

  await toClose.close();

  await archive.clear();

  if (tempDir != null) {
    await tempDir.delete(recursive: true);
  }
}