skip to Main Content

I have a C++ project that uses EMSDK to compile to WebAssembly. I have an asset that my project loads, a bundle.zip that I currently use the ‘–embed-file’ flag from the SDK at compile time, and this works.

However, I would like to know if there is a way, in HTML or JavaScript, to pre-load this asset. So, instead of having to rebuild my project every time I change something in ‘bundle.zip’, I would just upload it and keep the same ‘.wasm’.

Is this option available? When searching online, I only found questions and answers related to C#’s Blazor, which is not related to what I want.

A crucial detail is that my application needs to read this file from the file system, as if it were on the native platform, not on the web (it does a fopen).

2

Answers


  1. Chosen as BEST ANSWER

    After the response of @VonC, this is my final HTML

    <!doctype html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8" />
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
      <link rel="preload" href="/bundle.zip" as="fetch" type="application/octet-stream" crossorigin="anonymous" />
      <style>
        *,
        *::before,
        *::after {
          box-sizing: border-box;
        }
    
        * {
          margin: 0;
        }
    
        body {
          line-height: 1.5;
          -webkit-font-smoothing: antialiased;
        }
    
        canvas {
          display: block;
          max-width: 100%;
        }
    
        .container {
          display: flex;
          align-items: center;
          justify-content: center;
        }
      </style>
    </head>
    
    <body>
      <div class="container">
        <canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
        <script>
          const canvas = document.getElementById("canvas");
    
          var Module = {
            canvas,
            noInitialRun: true,
            onRuntimeInitialized: () => {
              fetch("/bundle.zip")
                .then((response) => response.arrayBuffer())
                .then((data) => {
                  const uint8View = new Uint8Array(data);
                  FS.writeFile("/bundle.zip", uint8View);
                  Module.callMain();
                });
            },
          };
        </script>
        <script src="carimbo.js"></script>
      </div>
    </body>
    
    </html>
    

  2. You might consider using JavaScript to fetch the ‘bundle.zip‘ file at runtime, and then use the Emscripten Filesystem API to make it accessible to your WebAssembly module.
    That way, you can change ‘bundle.zip‘ without recompiling your WebAssembly module.

    +-------------+     +-----------------+     +-------------+
    | C++ Project | --> | Compile to WASM | --> | Web Browser |
    +-------------+     +-----------------+     +-------------+
          |                   |                        |
          |                   |                        |
          |                   |                        +-- Fetch bundle.zip
          |                   |                        |   at runtime
          |                   |                        |
          |                   |                        +-- Write to Virtual FS
          |                   |                        |   (Emscripten FS API)
          |                   |                        |
          |                   |                        +-- Access via fopen()
    

    First, remove the --embed-file flag from your Emscripten build command. That will stop embedding ‘bundle.zip’ into your WebAssembly module.

    Check if, as commented, the --preload-file option mentioned in Emscripten » Porting » Files and File Systems » Packaging Files would work

    When compiling your project with Emscripten, use the --preload-file flag followed by the path to ‘bundle.zip‘. Emscripten then generates a ‘.data‘ file alongside your ‘.wasm‘ and ‘.js‘ files.

    emcc your_project.cpp -o your_project.html --preload-file path/to/bundle.zip
    

    The generated JavaScript loader code will automatically fetch the ‘.data‘ file when loading your WebAssembly module. That means ‘bundle.zip‘ is downloaded separately from your ‘.wasm‘ file, but in a way that is transparent to your application.

    The contents of ‘bundle.zip‘ are automatically unpacked into the Emscripten virtual filesystem at runtime. Your application should then use fopen to access the files as if they were on a regular filesystem.

    To update ‘bundle.zip‘, you simply replace the ‘.data‘ file on your server. There is no need to recompile your WebAssembly module unless the C++ code itself changes.


    Or use JavaScript to fetch ‘bundle.zip‘ when your web page loads. You can use the fetch API for that.

    Before you call any functions from your WebAssembly module that need ‘bundle.zip‘, make sure to mount a virtual filesystem and write your fetched file into it: that will make ‘bundle.zip‘ accessible as if it were part of a normal filesystem.

    Your C++ code should now be able to use fopen to open ‘bundle.zip‘, as it would on a native filesystem.

    // Fetch the bundle.zip
    fetch('path/to/bundle.zip')
      .then(response => response.arrayBuffer())
      .then(data => {
        // Convert the fetched data into a Uint8Array
        var uint8View = new Uint8Array(data);
    
        // Initialize the Emscripten file system
        FS.writeFile('/bundle.zip', uint8View);
    
        // Now you can call your WebAssembly functions that use fopen
        // For example: Module.ccall('yourFunction', 'returnType', ['arg1Type'], [arg1Value]);
      })
      .catch(err => console.error('Error fetching bundle.zip:', err));
    

    FS.writeFile (part of the Emscripten Filesystem API) is used to write the fetched file to the Emscripten virtual filesystem.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search