createStaticHandler function
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);
}
});
};
}