Skip to main content
Version: next

write

Write files to the device filesystem.

If the parent directory does not exist, it will be created.

If the file already exists, it will be overwritten.

interface AdbSyncWriteOptions {
filename: string;
file: ReadableStream<MaybeConsumable<Uint8Array>>;
type?: LinuxFileType;
permission?: number;
mtime?: number;
compression?: Compression.Format;
dryRun?: boolean;
}

declare class AdbSync {
write(options: AdbSyncWriteOptions): Promise<void>;
}

Options

  • filename: Path to the file on the device filesystem
  • file: File content. It uses the Consumable pattern.
  • type: File type, defaults to LinuxFileType.File. Can't be LinuxFileType.Directory.
  • permission: File permission, defaults to 0o644
  • mtime: File modification time, defaults to current time (in seconds since Unix epoch).
  • compression: The compression format to use for the file stream. Only available if device supports sendrecv_v2 feature.
  • dryRun: If true, the file will not be written to the device filesystem. This was added for debugging and benchmarking purposes.

Compression

The compression option allows runtime-selectable compression for push operations:

import { Compression } from "@yume-chan/adb";

// Check if a compression format is supported
if (Compression.canUseFormat(adb, Compression.Format.Brotli)) {
await sync.write({
filename: "/sdcard/large-file.bin",
file: stream,
compression: Compression.Format.Brotli,
});
}

Available compression formats:

  • Compression.Format.None - No compression (default)
  • Compression.Format.Brotli - Brotli compression
  • Compression.Format.Lz4 - LZ4 compression
  • Compression.Format.Zstd - Zstandard compression

Compression Format Selection

The compression option behavior depends on the protocol version and device capabilities:

1. When compression is undefined (default):

The library automatically selects the best compression format based on:

  • Device capabilities (sendrecv_v2 feature support)
  • Runtime compression library availability
  • Performance considerations
// compression: undefined (default) - auto-selects best format
await sync.write({
filename: "/sdcard/file.bin",
file: stream,
// compression is undefined by default
});

Auto-selection priority:

  1. If device supports sendrecv_v2 and Zstd is available → Zstd
  2. If device supports sendrecv_v2 and LZ4 is available → LZ4
  3. If device supports sendrecv_v2 and Brotli is available → Brotli
  4. Otherwise → No compression (Compression.Format.None)
info

The auto-selection priority follows the Android ADB source code, which prefers Zstd over LZ4 and Brotli.

2. When using protocol version 1 (legacy devices):

If the device doesn't support sendrecv_v2 feature (Android 9 and below), compression is automatically disabled regardless of the compression option value:

// Device doesn't support sendrecv_v2
// compression option is ignored, no compression is used
await sync.write({
filename: "/sdcard/file.bin",
file: stream,
compression: Compression.Format.Brotli, // Ignored on v1 devices
});

3. When compression is set to an unsupported format:

If you explicitly set a compression format that is not supported by either the device or runtime, an error is thrown:

// Device supports sendrecv_v2, but runtime doesn't support Brotli
await sync.write({
filename: "/sdcard/file.bin",
file: stream,
compression: Compression.Format.Brotli, // Throws Error!
});

Error message example:

Error: Compression type brotli is not supported

To avoid this, always check support before using a specific format:

import { Compression } from "@yume-chan/adb";

// Check both device and runtime support
if (!Compression.canUseFormat(adb, Compression.Format.Brotli)) {
throw new Error("Brotli compression is not available");
}

await sync.write({
filename: "/sdcard/file.bin",
file: stream,
compression: Compression.Format.Brotli,
});

4. When compression is explicitly set to Compression.Format.None:

Compression is disabled even if the device supports it:

// Explicitly disable compression
await sync.write({
filename: "/sdcard/file.bin",
file: stream,
compression: Compression.Format.None, // No compression
});

This is useful when:

  • Compressing already-compressed data (images, videos, archives)
  • Debugging compression issues
  • Benchmarking performance differences
info

Compression requires both device support (sendrecv_v2 feature) and runtime support for the compression library. Use Compression.canUseFormat() to check before using.

Example

Write a file

import { encodeUtf8 } from "@yume-chan/adb";

const file = new ReadableStream({
start(controller) {
controller.enqueue(encodeUtf8("Hello, world!"));
controller.close();
},
});

await sync.write({
filename: "/sdcard/Download/hello.txt",
file,
});
Equivalent ADB Command
echo "Hello, world!" > hello.txt
adb push hello.txt /sdcard/Download/hello.txt

To write a symbolic link, write the target path as content and set type to LinuxFileType.SymbolicLink.

import { encodeUtf8 } from "@yume-chan/adb";

await sync.write({
filename: "/sdcard/Download/hello.txt",
file: new ReadableStream({
start(controller) {
controller.enqueue(encodeUtf8("/sdcard/Download/target.txt"));
controller.close();
},
}),
type: LinuxFileType.SymbolicLink,
});
Equivalent ADB Command
adb shell ln -s /sdcard/Download/target.txt /sdcard/Download/hello.txt

(Web) upload a user-selected file

import { type ReadableStream } from "@yume-chan/stream-extra";

const input = document.createElement("input");
input.type = "file";
input.accept = "*/*";
input.onchange = async () => {
const file = input.files[0];
if (!file) {
return;
}

await sync.write({
filename: `/sdcard/Download/${file.name}`,
file: file.stream(),
});
};
input.click();

Internal API

info

Note: This is an internal API that is usually not needed directly. Most users should use the public API (adb.sync.write) instead.

The write method uses AdbSync.Send.send() internally, which operates on a SocketPool:

import type { SocketPool } from "@yume-chan/adb";
import { AdbSync, Compression } from "@yume-chan/adb";

declare const pool: SocketPool;
declare const filename: string;
declare const file: ReadableStream<Uint8Array>;

await AdbSync.Send.send({
pool,
filename,
file,
version: 2, // or 1 for legacy protocol
compression: Compression.Format.Brotli, // optional
dryRun: false, // optional
});

Protocol versions

Version 1 (legacy):

  • Uses SEND request
  • No compression support
  • Compatible with all Android versions

Version 2:

  • Uses SND2 request
  • Supports compression (Brotli, LZ4, Zstd)
  • Requires sendrecv_v2 feature (Android 10+)
  • Supports dryRun mode for benchmarking

How it works

  1. Acquires a socket from the pool
  2. Sends a SEND or SND2 request with filename and mode
  3. Streams file data in chunks (default 64KB packets)
  4. Sends DONE with mtime when complete
  5. Waits for OKAY response
  6. Automatically releases the socket back to the pool

The socket is automatically released after completion or error. If an error occurs during transmission, the socket is discarded to prevent connection corruption.