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.

Two APIs are provided:

  • write: returns a Promise for simple and quick file writing.
  • createWritable: returns a SendSession for advanced use cases, allowing manual stream control and live progress tracking.

write

interface AdbSyncWriteOptions {
path: string;
type?: LinuxFileType;
permission?: number;
mtime?: number;
compression?: Compression.Format;
dryRun?: boolean;
}

declare class AdbSync {
write(
options: AdbSyncWriteOptions & {
readable: ReadableStream<MaybeConsumable<Uint8Array>>;
},
): Promise<Omit<SendSession, "writable">>;
}

Options

  • path: Path to the file on the device filesystem. Unlike cp or adb push commands, it can't point to an existing directory, it must be the full path to the file to be created.
  • readable: 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 specifies the compression format to use when transferring the file:

  • If compression is undefined, the best format supported by both the device and the current runtime is selected automatically.
  • If Compression.Format.None is specified, compression is disabled.
  • Otherwise the explicitly specified format is used. If the format is not supported by either the device or runtime, an error is thrown.

See Compression for more details.

Example

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

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

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

createWritable

For advanced use cases, createWritable returns a SendSession that allows manual control over the stream and progress tracking.

interface SendSession {
/**
* The writable stream to write the file content into.
*/
readonly writable: WritableStream<MaybeConsumable<Uint8Array>>;
/**
* Gets the number of bytes written into `writable`.
*/
readonly bytesWritten: number;
/**
* Gets the compression format used (might be `None`).
*/
readonly compression?: Compression.Format | undefined;
/**
* Gets the size of the compressed data sent to the device.
*/
readonly bytesCompressed: number;
}

declare class AdbSync {
createWritable(options: AdbSyncWriteOptions): Promise<SendSession>;
}

bytesWritten and bytesCompressed are live getters. You can poll them while writing to the writable stream to calculate real-time compression ratios or show progress in a UI.

Compression

See compression section above for details.

Example

import type { AdbSync } from "@yume-chan/adb";
import { MaybeConsumable } from "@yume-chan/stream-extra";

declare const sync: AdbSync.Service;
declare const readable: ReadableStream<Uint8Array>;

const session = await sync.createWritable({
path: "/sdcard/large-file.bin",
});

const progressInterval = setInterval(() => {
const ratio = session.bytesCompressed / session.bytesWritten;
console.log(`Progress: ${session.bytesWritten} bytes, Ratio: ${ratio.toFixed(2)}`);
}, 1000);

try {
await readable.pipeTo(session.writable);
} finally {
clearInterval(progressInterval);
}

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 path: string;
declare const readable: ReadableStream<Uint8Array>;

const session = await AdbSync.Send.send({
pool,
path,
readable,
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 path 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.