Houdini 20.0 Pythonスクリプト hou hou.webServer

hou.webServer.fileResponse HOM function

ファイルの内容を送信するResponseオブジェクトを生成します。

On this page

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

file_path

クライアントにダウンロードするディスク上のファイルのファイルパス。

content_type

レスポンスのMIMEコンテンツタイプ。 これがNone(デフォルト)の場合、この関数はmimetypes.guess_type()を使用して、ディスクファイルの拡張子からMIMEタイプを推測します。

delete_file

これがTrueの場合、サーバーは、ファイルをクライアントに送信した後にそのファイルを削除します。 これは、一時ファイルには便利ですが、コードの挙動を変更した時にこれを無効にするのを忘れた場合に非常に危険になる場合があります。

download_as_filename

クライアントが名前を付けてダウンロードするファイルのファイル名文字列をオプションで指定することができます。 サーバーはContent-Dispositionヘッダを送信します。

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)

相対ファイルを提供する方法

ハンドラーを含んだPythonスクリプトファイルを基準とした相対パスを使用してファイルを提供したい場合があります。 通常では、__file__変数には現在実行されているPythonスクリプトファイルのパスが格納されるので、os.path.dirname(__file__)を使用することで、そのPythonスクリプトファイルが含まれているディレクトリを取得することができます。

しかし、例えばコマンドラインからPythonスクリプトを使ってHoudiniを起動した場合などでは、__file__が定義されていない場合があります。 これを補足するには、以下の関数を使用すると良いでしょう:

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)

画像をレンダリングするサンプル

import os
import tempfile

@hou.webServer.urlHandler("/tommy.jpg")
def tommy(request):
    """Mantraを使用して、Tommyテストジオメトリの画像をレンダリングします。"""
    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ビューアのサンプル

このサンプルでは、GLTF ROPを使用してCragテストジオメトリをGLTFフォーマットに保存し、 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))

    # GLTF ROPを使用して、一時GLTFファイルを書き出します。
    # このROPは、処理時に.binとテクスチャを生成します。
    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))

    # ここではJSONを読み込んでURIを調整することもできたのですが、その必要がなかったことを注記しておきます。
    return hou.webServer.fileResponse(
        gltf_temp_file, "application/json",
        delete_file=True)


@hou.webServer.urlHandler("/static/output", is_prefix=True)
def temp_output(request):
    # このURLハンドラーは、GLTF用に一時的に.binとテクスチャファイルを用意するために使用します。
    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

クラス

開始と停止

Webリクエストの処理とレスポンス

APIコール

  • hou.webServer.apiFunction()

    HoudiniのWebサーバー上のAPIエンドポイントを介してコール可能な関数のデコレータで、JSONレスポンスまたはバイナリレスポンスを返します。

  • hou.webServer.APIError

    apiFunctionハンドラーでこの例外を引き起こしてエラーを示します。