Although the SWI-Prolog web-server is intended to serve documents that need to be computed dynamically, serving plain files is sometimes necessary. There are several options to serve files.
Locating physical files
Before we can serve files, we first need to be able to find them on the file-system. This is a general topic, but we discuss it here because it is especially relevant to serving files. There are three options:
- Specify absolute file names (i.e., files that begin with / (or <drive>:/ in windows). This is typically a bad idea as it makes moving your server to another environment complicated.
- Specify relative file names, where the server's working directory is the basis. This is much better than using absolute path, but still makes moving things around complicated.
- Use file-path mechanism implemented by absolute_file_name/3 (search over a path) and file_search_path/2 (define paths). This is a bit similar to the PATH variable used to find executables in operating systems. It allows for multiple paths and the specification may be non-deterministic. This non-determinism can be used to find files in multiple physical locations using the same specification. A good example of this can be found in serve_files_in_directory/2.
All code snippets below assume a running server and the library(http/http_dispatch) loaded. This means that running these examples require a file with this content to be loaded (8080 is the port; change as you please).
:- use_module(library(http/thread_httpd)). :- use_module(library(http/http_dispatch)). :- initialization http_server(http_dispatch, [port(8080)]).
All snippets are supposed to be loaded from a source-file, as opposed to be typed in the console.
Serve a few files
Sometimes you have a fully dynamic server that needs to serve, for
example, a few images or CSS files. You can solve this directly using
http_reply_file/3. The example below serves
/favicon.ico from the
:- http_handler('/favicon.ico'), http_reply_file('favicon.ico', ), ).
Serving many `server support' files
In the next example, we create a serve that serves images on
/images/... from a directory
/srv/htdocs/icons/.... Note that
this server has no means for browsing by the user. It answers the file
or 404, which is typically precisely what we want for server resource
First, we specify a path alias for the location handled by the server.
:- use_module(library(http/http_path)). http:location(images, root(images), ).
Next, we specify a file alias for the location on the file-system. We
do this in two steps: (1) we define an alias for our entire document-set
and (2) then we define the target
icons alias relative to this
central alias. This two-step approach allows for updating the server to
a modified environment easily. Note that the
document_root alias can
be in a different Prolog file (e.g., a global configuration file).
:- multifile user:file_search_path/2. user:file_search_path(document_root, '/srv/htdocs'). user:file_search_path(icons, document_root(icons)).
Finally, serve_files_in_directory combines the two aliases. It is common to use the same alias name for both the HTTP path and file. We used two different ones in this example to clarify their roles.
:- http_handler(images(.), serve_files_in_directory(icons), [prefix]).
Serving a user-browsable directory hierarchy
If you want to give the user access to a set of files, the above is unsuitable because it does not support directory browsing. SWI-Prolog 5.11.29 and 5.10.6 provide library(http/http_files) to solve this problem. The predicate http_reply_from_files/3 combines http_reply_file/3 with http_reply_dirindex/3. Here is the complete code to serve files to a server running at port 8080 from the local directory. Note that the server disallows access to ../, etc., to protect sensitive files on the system.
:- use_module(library(http/thread_httpd)). :- use_module(library(http/http_dispatch)). :- use_module(library(http/http_files)). :- http_handler(root(.), http_reply_from_files('.', ), [prefix]). :- initialization http_server(http_dispatch, [port(8080)]).