createStaticHandler function

Handler createStaticHandler(
  1. String fileSystemPath, {
  2. bool serveFilesOutsidePath = false,
  3. String? defaultDocument,
  4. bool listDirectories = false,
  5. bool useHeaderBytesForContentType = false,
  6. MimeTypeResolver? contentTypeResolver,
})

Creates a Shelf Handler that serves files from the provided fileSystemPath.

Accessing a path containing symbolic links will succeed only if the resolved path is within fileSystemPath. To allow access to paths outside of fileSystemPath, set serveFilesOutsidePath to true.

When a existing directory is requested and a defaultDocument is specified the directory is checked for a file with that name. If it exists, it is served.

If no defaultDocument is found and listDirectories is true, then the handler produces a listing of the directory.

If useHeaderBytesForContentType is true, the contents of the file will be used along with the file path to determine the content type.

Specify a custom contentTypeResolver to customize automatic content type detection.

Implementation

Handler createStaticHandler(String fileSystemPath,
    {bool serveFilesOutsidePath = false,
    String? defaultDocument,
    bool listDirectories = false,
    bool useHeaderBytesForContentType = false,
    MimeTypeResolver? contentTypeResolver}) {
  final rootDir = Directory(fileSystemPath);
  if (!rootDir.existsSync()) {
    throw ArgumentError('A directory corresponding to fileSystemPath '
        '"$fileSystemPath" could not be found');
  }

  fileSystemPath = rootDir.resolveSymbolicLinksSync();

  if (defaultDocument != null) {
    if (defaultDocument != p.basename(defaultDocument)) {
      throw ArgumentError('defaultDocument must be a file name.');
    }
  }

  final mimeResolver = contentTypeResolver ?? _defaultMimeTypeResolver;

  return (Request request) {
    final segs = [fileSystemPath, ...request.url.pathSegments];

    final fsPath = p.joinAll(segs);

    final entityType = FileSystemEntity.typeSync(fsPath);

    File? fileFound;

    if (entityType == FileSystemEntityType.file) {
      fileFound = File(fsPath);
    } else if (entityType == FileSystemEntityType.directory) {
      fileFound = _tryDefaultFile(fsPath, defaultDocument);
      if (fileFound == null && listDirectories) {
        final uri = request.requestedUri;
        if (!uri.path.endsWith('/')) return _redirectToAddTrailingSlash(uri);
        return listDirectory(fileSystemPath, fsPath);
      }
    }

    if (fileFound == null) {
      return Response.notFound('Not Found');
    }
    final file = fileFound;

    if (!serveFilesOutsidePath) {
      final resolvedPath = file.resolveSymbolicLinksSync();

      // Do not serve a file outside of the original fileSystemPath
      if (!p.isWithin(fileSystemPath, resolvedPath)) {
        return Response.notFound('Not Found');
      }
    }

    // when serving the default document for a directory, if the requested
    // path doesn't end with '/', redirect to the path with a trailing '/'
    final uri = request.requestedUri;
    if (entityType == FileSystemEntityType.directory &&
        !uri.path.endsWith('/')) {
      return _redirectToAddTrailingSlash(uri);
    }

    return _handleFile(request, file, () async {
      if (useHeaderBytesForContentType) {
        final length =
            math.min(mimeResolver.magicNumbersMaxLength, file.lengthSync());

        final byteSink = ByteAccumulatorSink();

        await file.openRead(0, length).listen(byteSink.add).asFuture<void>();

        return mimeResolver.lookup(file.path, headerBytes: byteSink.bytes);
      } else {
        return mimeResolver.lookup(file.path);
      }
    });
  };
}