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 filesystemfile: File content. It uses the Consumable pattern.type: File type, defaults toLinuxFileType.File. Can't beLinuxFileType.Directory.permission: File permission, defaults to0o644mtime: 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 supportssendrecv_v2feature.dryRun: Iftrue, 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 compressionCompression.Format.Lz4- LZ4 compressionCompression.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_v2feature 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:
- If device supports
sendrecv_v2and Zstd is available → Zstd - If device supports
sendrecv_v2and LZ4 is available → LZ4 - If device supports
sendrecv_v2and Brotli is available → Brotli - Otherwise → No compression (
Compression.Format.None)
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
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,
});
echo "Hello, world!" > hello.txt
adb push hello.txt /sdcard/Download/hello.txt
Write a symbolic link
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,
});
adb shell ln -s /sdcard/Download/target.txt /sdcard/Download/hello.txt
(Web) upload a user-selected file
- JavaScript
- TypeScript
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();
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
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
SENDrequest - No compression support
- Compatible with all Android versions
Version 2:
- Uses
SND2request - Supports compression (Brotli, LZ4, Zstd)
- Requires
sendrecv_v2feature (Android 10+) - Supports
dryRunmode for benchmarking
How it works
- Acquires a socket from the pool
- Sends a
SENDorSND2request with filename and mode - Streams file data in chunks (default 64KB packets)
- Sends
DONEwith mtime when complete - Waits for
OKAYresponse - 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.