Houdini 20.0 Python scripting hou hou.webServer

hou.webServer.fileResponse HOM function

Generates a Response object that sends the contents of a file.

On this page

fileResponse(file_path, content_type=None, delete_file=False, download_as_filename=None): hou.webServer.Response

file_path

The file path of the file on disk to download to the client.

content_type

The MIME content type of the response. If this is None (the default), the function uses mimetypes.guess_type() to guess the MIME type from the disk file’s extension.

delete_file

If this is True, the server deletes the file after sending it to the client. This is useful for temp files, but could be very dangerous if you change how the code works but forget to turn this off.

download_as_filename

You can optionally supply the filename string the client will download the file as. The server will send a Content-Disposition header.

import hou


@hou.webServer.urlHandler("/textures", prefix=True)
def texture_download(request):
    path = request.path()
    assert path.startswith("/textures")
    parm_name = path[10:]
    if not parm_name:
        return hou.webServer.notFoundResponse(request)

    node = hou.node("/mat/my_material")
    texture_parm = node.parm(parm_name)
    texture_path = texture_parm.evalAsString()

    return fileResponse(request, texture_path)

Serving relative files

Sometimes you want to serve files using a path relative to the Python script file containing the handler. Usually the __file__ variable contains the path of the currently running file, so you can get the directory containing it using os.path.dirname(__file__).

However, sometimes __file__ is not defined, such as when you launch Houdini from the command line with a Python script. To compensate for this, you can use the following function:

import os
import inspect


def script_file_dir():
    try:
        file_path = __file__
    except NameError:
        file_path = inspect.getfile(inspect.currentframe())

    if not os.path.isabs(file_path):
        file_path = os.path.normpath(os.path.join(os.getcwd(), file_path)))

    return os.path.dirname(file_path)

Rendered image example

import os
import tempfile

@hou.webServer.urlHandler("/tommy.jpg")
def tommy(request):
    """Render an image of the Tommy test geometry using Mantra."""
    rop = hou.node("/out/tommy_rop")
    if rop is None:
        rop = create_tommy_scene()

    rop.render()
    return hou.webServer.fileResponse(rop.evalParm("vm_picture"))

def create_tommy_scene():
    sop = hou.node("/obj").createNode("geo").createNode("testgeometry_tommy")

    cam = hou.node("/obj").createNode("cam", "tommy_cam")
    cam.parmTuple("t").set((0, 1.3, 2.3))

    output_file = os.path.join(tempfile.gettempdir(), "tommy.jpg")
    rop = hou.node("/out").createNode("ifd", "tommy_rop")
    rop.parm("camera").set(cam.path())
    rop.parm("vobject").set(sop.parent().name())
    rop.parm("vm_picture").set(output_file)

    return rop

3D web viewer example

This example uses the GLTF ROP to save the Crag test geometry to GTLF format and displays it in the browser using Babylon.js.

server.py

import os
import tempfile
import json

import hou

import webutils


OUTPUT_DIR = os.path.join(webutils.script_file_dir(), "static", "temp")


@hou.webServer.urlHandler("/")
def index_view(request):
    return hou.webServer.redirect(request, "/static/index.html")


@hou.webServer.urlHandler("/static/output/model.gltf")
def gltf_view(request):
    frame = float(request.GET().get("frame", 1))

    # Use the GLTF ROP to write to a temporary gltf file.  It will create
    # the .bin and any textures during the process.
    gltf_temp_file = temp_file_path(".gltf")
    hou.parm("/out/gltf1/file").set(gltf_temp_file)
    hou.node("/out/gltf1").render(frame_range=(frame, frame))

    # Note that we could read in the JSON and adjust URIs but we don't need to.
    return hou.webServer.fileResponse(
        gltf_temp_file, "application/json",
        delete_file=True)


@hou.webServer.urlHandler("/static/output", is_prefix=True)
def temp_output(request):
    # This URL handler is used to serve the temporary .bin and texture files
    # for the GTLF.
    bin_file_path = os.path.join(OUTPUT_DIR, os.path.basename(request.path()))
    response = hou.webServer.fileResponse(
        bin_file_path, "application/octet-stream",
        delete_file=True)
    return response


def temp_file_path(suffix):
    with tempfile.NamedTemporaryFile(
            dir=OUTPUT_DIR, suffix=suffix) as temp_file:
        return temp_file.name


def initialize_scene():
    hou.node("/obj").createNode("geo").createNode("testgeometry_crag")
    hou.node("/out").createNode("gltf")


def run_server():
    if not os.path.exists(OUTPUT_DIR):
        os.makedirs(OUTPUT_DIR)
    initialize_scene()
    hou.webServer.registerStaticFilesDirectory(
        os.path.join(webutils.script_file_dir(), "static"))
    hou.webServer.run(8008, debug=True)


if __name__ == "__main__":
    run_server()

static/index.html

<script src="https://cdn.babylonjs.com/viewer/babylon.viewer.js"></script>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="/static/index.js"></script>

<babylon id="babylon-viewer"></babylon>
<br>Frame <input onchange="set_frame(parseInt(this.value, 10))"
    value="1" size="4">

static/index.js

var app = {
    frame: 1,
    viewer: null,
};

BabylonViewer.viewerManager
    .getViewerPromiseById('babylon-viewer').then(function (viewer)
{
    app.viewer = viewer;
    $(viewer.containerElement).css("height", "90%");
    viewer.onEngineInitObservable.add(function (scene) {
        reload_model();
    });
});

function set_frame(frame)
{
    app.frame = frame;
    reload_model();
}

function reload_model()
{
    app.viewer.loadModel({url: "/static/output/model.gltf?frame=" + app.frame});
}
See also

hou.webServer

Classes

Starting and Stopping

Handling Web Requests and Returning Responses

API Calls

  • hou.webServer.apiFunction()

    Decorator for functions that can be called through an API endpoint on Houdini’s web server, returning JSON or binary responses.

  • hou.webServer.APIError

    Raise this exception in apiFunction handlers to indicate an error.