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.


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

  fileSystemPath = rootDir.resolveSymbolicLinksSync();

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

  contentTypeResolver ??= _defaultMimeTypeResolver;

  return (Request request) {
    var segs = [fileSystemPath]..addAll(request.url.pathSegments);

    var fsPath = p.joinAll(segs);

    var entityType = FileSystemEntity.typeSync(fsPath, followLinks: true);

    File file;

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

    if (file == null) {
      return new Response.notFound('Not Found');

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

      // Do not serve a file outside of the original fileSystemPath
      if (!p.isWithin(fileSystemPath, resolvedPath)) {
        return new 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 '/'
    var uri = request.requestedUri;
    if (entityType == &&
        !uri.path.endsWith('/')) {
      return _redirectToAddTrailingSlash(uri);

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

        var byteSink = new ByteAccumulatorSink();

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

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