# How to Implement File Upload and Download Operations Within OpenSandbox Sandboxes

> Implement file upload and download in OpenSandbox using the execd daemon's HTTP filesystem API. Use FilesystemAdapter for secure and efficient file operations.

- Repository: [Alibaba/OpenSandbox](https://github.com/alibaba/OpenSandbox)
- Tags: how-to-guide
- Published: 2026-03-08

---

**Developers can implement file upload and download operations within OpenSandbox sandboxes by using the HTTP-based filesystem API exposed by the execd daemon, utilizing the `FilesystemAdapter` class for multipart uploads via `POST /files/upload` and ranged downloads via `GET /files/download`.**

The Alibaba OpenSandbox project provides a secure, isolated execution environment where the sandbox's file system is treated as a regular HTTP service. To implement file upload and download operations within OpenSandbox sandboxes, developers interact with the **execd** daemon running inside each container through dedicated REST endpoints. The Python SDK abstracts these low-level HTTP calls through the `FilesystemAdapter` class and high-level `WriteEntry` models, enabling both single-file operations and batch transfers with full metadata control.

## Understanding OpenSandbox's File System Architecture

OpenSandbox exposes the sandbox file system through the execd daemon as a stateless HTTP service. This design allows external clients to perform file operations without requiring direct shell access or volume mounts.

The two primary endpoints for file transfer are:

| Operation | HTTP Method | Endpoint | Purpose |
|-----------|-------------|----------|---------|
| **Upload** | `POST` | `/files/upload` | Transfers one or more files with JSON metadata (target path, permissions, ownership) |
| **Download** | `GET` | `/files/download` | Retrieves raw file bytes; supports the HTTP `Range` header for partial content |

According to the OpenSandbox source code, the `FilesystemAdapter` in [`opensandbox/adapters/filesystem_adapter.py`](https://github.com/alibaba/OpenSandbox/blob/main/opensandbox/adapters/filesystem_adapter.py) manages the `httpx.AsyncClient` connection to these endpoints, handling authentication and request construction internally.

## Uploading Files to an OpenSandbox Sandbox

Uploads are handled as multipart/form-data requests where each file is accompanied by a metadata JSON object describing the destination path and file attributes.

### Constructing WriteEntry Objects

Before uploading, developers create `WriteEntry` instances defined in [`opensandbox/models/filesystem.py`](https://github.com/alibaba/OpenSandbox/blob/main/opensandbox/models/filesystem.py). Each entry encapsulates the target path, data payload, and optional Unix permissions.

```python
from opensandbox.models.filesystem import WriteEntry

entry = WriteEntry(
    path="/workspace/hello.txt",
    data=b"hello execd\n",
    mode=0o644,
    owner="sandbox",
    group="sandbox"
)

```

The `data` field accepts `bytes`, `str`, or binary streams, allowing flexible input sources.

### Using FilesystemAdapter for Batch Uploads

The `FilesystemAdapter.write_files` method (lines 71-84 in [`opensandbox/adapters/filesystem_adapter.py`](https://github.com/alibaba/OpenSandbox/blob/main/opensandbox/adapters/filesystem_adapter.py)) constructs the multipart payload and executes the `POST` request to `/files/upload`.

```python
from opensandbox import OpenSandbox
from opensandbox.config import ConnectionConfig
from opensandbox.models.filesystem import WriteEntry

# Initialize connection

cfg = ConnectionConfig(
    protocol="http",
    host="localhost",
    port=8080,
    request_timeout=30,
)

sandbox = await OpenSandbox.create(
    connection_config=cfg,
    sandbox_id="my-sandbox"
)

# Upload multiple files in one request

entries = [
    WriteEntry(path="/workspace/config.json", data=b'{"key": "value"}', mode=0o600),
    WriteEntry(path="/workspace/script.py", data=open("local_script.py", "rb"), mode=0o755),
]

await sandbox.files.write_files(entries)

```

The generated OpenAPI client in [`api/execd/api/filesystem/upload_file.py`](https://github.com/alibaba/OpenSandbox/blob/main/api/execd/api/filesystem/upload_file.py) (lines 30-44) handles the low-level HTTP serialization of these multipart parts.

## Downloading Files from an OpenSandbox Sandbox

Download operations retrieve file contents through the `GET /files/download` endpoint, with full support for HTTP range requests and streaming.

### Full File Downloads

For complete file retrieval, the `FilesystemAdapter` uses `_build_download_request` (lines 59-68 in [`opensandbox/adapters/filesystem_adapter.py`](https://github.com/alibaba/OpenSandbox/blob/main/opensandbox/adapters/filesystem_adapter.py)) to construct the request, then returns the raw bytes.

```python

# Download entire file as bytes

content = await sandbox.files.read_bytes("/workspace/hello.txt")
print(content.decode("utf-8"))

```

### Partial Downloads with HTTP Range Headers

OpenSandbox supports the standard HTTP `Range` header for resumable transfers or selective byte extraction. Pass the range specification to `read_bytes` via the `range_header` parameter.

```python

# Retrieve bytes 0-4 (first 5 bytes)

partial = await sandbox.files.read_bytes(
    "/workspace/large.bin",
    range_header="bytes=0-4"
)
print(f"First 5 bytes: {partial}")

```

The generated client in [`api/execd/api/filesystem/download_file.py`](https://github.com/alibaba/OpenSandbox/blob/main/api/execd/api/filesystem/download_file.py) (lines 30-48) serializes these range headers into the outgoing HTTP request.

### Streaming Large Files

To avoid loading large files into memory, use the streaming interface `read_bytes_stream`, which yields chunks asynchronously.

```python
async for chunk in sandbox.files.read_bytes_stream(
    "/workspace/big.dat",
    chunk_size=4 * 1024 * 1024  # 4 MiB chunks

):
    process_chunk(chunk)  # Your processing logic here

```

This method utilizes `httpx`'s streaming capabilities with `client.send(..., stream=True)` under the hood.

## Key Source Files and Implementation Details

The following files in the `alibaba/OpenSandbox` repository define the file transfer implementation:

| File Path | Description |
|-----------|-------------|
| [`sdks/sandbox/python/src/opensandbox/adapters/filesystem_adapter.py`](https://github.com/alibaba/OpenSandbox/blob/main/sdks/sandbox/python/src/opensandbox/adapters/filesystem_adapter.py) | Core adapter containing `write_files` (lines 71-84) and `_build_download_request` (lines 59-68) |
| [`sdks/sandbox/python/src/opensandbox/api/execd/api/filesystem/upload_file.py`](https://github.com/alibaba/OpenSandbox/blob/main/sdks/sandbox/python/src/opensandbox/api/execd/api/filesystem/upload_file.py) | Generated OpenAPI client for `POST /files/upload` (lines 30-44) |
| [`sdks/sandbox/python/src/opensandbox/api/execd/api/filesystem/download_file.py`](https://github.com/alibaba/OpenSandbox/blob/main/sdks/sandbox/python/src/opensandbox/api/execd/api/filesystem/download_file.py) | Generated OpenAPI client for `GET /files/download` (lines 30-48) |
| [`sdks/sandbox/python/src/opensandbox/models/filesystem.py`](https://github.com/alibaba/OpenSandbox/blob/main/sdks/sandbox/python/src/opensandbox/models/filesystem.py) | Data models including `WriteEntry` for upload metadata |
| [`sdks/sandbox/python/src/opensandbox/services/filesystem.py`](https://github.com/alibaba/OpenSandbox/blob/main/sdks/sandbox/python/src/opensandbox/services/filesystem.py) | Abstract `Filesystem` service interface exposed as `sandbox.files` |
| [`components/execd/tests/smoke_api.py`](https://github.com/alibaba/OpenSandbox/blob/main/components/execd/tests/smoke_api.py) | End-to-end tests demonstrating raw HTTP upload/download |

## Summary

- OpenSandbox exposes sandbox file systems via HTTP endpoints served by the **execd** daemon, eliminating the need for direct container access.
- **Uploads** use `POST /files/upload` with multipart/form-data payloads containing JSON metadata and raw file bytes, implemented in `FilesystemAdapter.write_files`.
- **Downloads** use `GET /files/download` with support for HTTP `Range` headers for partial content, implemented in the adapter's download methods.
- The Python SDK provides high-level abstractions (`sandbox.files.write_file`, `read_bytes`, `read_bytes_stream`) that wrap the low-level HTTP interactions.
- All file operations support Unix permissions, ownership, and streaming for memory-efficient handling of large files.

## Frequently Asked Questions

### What HTTP endpoints does OpenSandbox use for file operations?

OpenSandbox uses two primary REST endpoints exposed by the execd daemon: `POST /files/upload` for sending files to the sandbox and `GET /files/download` for retrieving them. Both endpoints accept standard HTTP headers, with the download endpoint supporting `Range` headers for partial content requests.

### How do I upload multiple files in a single request?

Use the `write_files` method of the `FilesystemAdapter` (accessed via `sandbox.files.write_files`) and pass a list of `WriteEntry` objects. Each `WriteEntry` specifies the target path, data payload, and file permissions. The SDK automatically constructs a multipart/form-data request containing all files and their metadata, sending them to the `POST /files/upload` endpoint in one HTTP call.

### Does OpenSandbox support resumable or partial file downloads?

Yes, the download implementation supports HTTP `Range` headers, allowing you to request specific byte ranges of a file. When calling `sandbox.files.read_bytes`, pass the `range_header` parameter with a value like `"bytes=0-1023"` to retrieve partial content. This enables resumable downloads and memory-efficient processing of large files without loading them entirely into RAM.

### What is the maximum file size supported for uploads?

The OpenSandbox source code does not enforce a specific file size limit within the SDK itself; constraints depend on the execd daemon's configuration and the underlying storage available to the sandbox container. For large files, use streaming uploads by passing file-like objects (binary streams) as the `data` parameter in `WriteEntry` rather than loading entire files into memory as bytes.