HarmonyOS —— Remote Communication Kit 流式传输(Streaming)实战笔记
HarmonyOS —— Remote Communication Kit 流式传输(Streaming)实战笔记
这一节可以直接理解成:
“不用一次性把请求/响应全准备好,边产出边发、边收到边处理”,
非常适合:大文件传输、日志/埋点上报、实时数据/直播等场景。
一、流式传输是什么?什么时候用它?
在 Remote Communication Kit 里,HTTP 流式传输(Streaming)允许:
- 请求端(上传):一边生成数据,一边写入流 → 框架在后台 IO 线程异步发送;
- 响应端(下载):一边从网络读取,一边写入你指定的缓冲区 / 文件 / 自定义流。
相比“普通一次性请求”的好处:
- 不用一次性把数据都放到内存里(更节省内存)
- 能更快看到“首响应”,体验上更“实时”
- 可以做 实时日志上传、逐行发送、逐 chunk 下载,配合前面讲的断点续传、暂停恢复,能撑起一个比较专业的下载器 / 上传模块。
设备 & 版本限制依旧是熟悉那句话:
同步读写流能力支持 Phone / 2in1 / Tablet / Wearable,
自 5.1.1(19) 起,新增支持 TV。
二、基于缓冲区队列的流式传输:NetworkInputQueue / NetworkOutputQueue
这一套可以简单理解为:
NetworkInputQueue:同步写队列 —— 我负责往里丢数据,RCP 帮我按时发出去;NetworkOutputQueue:同步读队列 —— 服务器回来的数据,先缓存在队列里,按需读。
1. 同步写:NetworkInputQueue → 流式上传
示例:
import { rcp } from '@kit.RemoteCommunicationKit';
export const testNetworkInputQueue = () => {
// 创建同步写队列对象
const networkInputQueue = new rcp.NetworkInputQueue();
// 模拟文件通过同步读写流上传场景,将数据写入队列
let counter = 0;
const interval = setInterval(() => {
// 每秒往队列里写一段数据
networkInputQueue.write('a counter ' + counter++);
console.info(`networkInputQueue write`);
if (counter === 10) {
clearInterval(interval);
// 写完记得关闭队列,告诉 RCP 不会再有新数据
networkInputQueue.close();
}
}, 1000);
try {
// 创建 session
const session = rcp.createSession();
console.info(`Post start.`);
// 发起 POST 请求,同时把 networkInputQueue 作为请求体
session.post('https://httpbin.org/anything', networkInputQueue).then((response) => {
console.info(`Response status code is: ${response.statusCode}`);
if (response && response.statusCode === 200) {
console.info(`Post succeeded! response: ${response.toString()}`);
} else {
console.error(`Post failed.`);
}
session.close();
}).catch((error: Error) => {
console.error(`Post error: ${JSON.stringify(error)}`);
session.close();
});
} catch (error) {
console.error(`create session error: ${JSON.stringify(error)}`);
}
}
这一段在干什么:
- 先创建一个
NetworkInputQueue:- 它是“可写队列”,你可以随时
write()一段数据进去; - RCP 的 IO 线程会在合适的时机把队列里的数据 打包发到网络上。
- 它是“可写队列”,你可以随时
- 用
setInterval每秒往里写一段字符串(模拟“流式产生数据”的场景,比如日志流、实时采集数据)。 - 写完 10 次之后,调用
networkInputQueue.close()告诉框架:后面没有更多数据了。 session.post(url, networkInputQueue):- 请求体直接来自这个队列;
- 队列写入和网络发送可以并行进行,无需等数据全部准备完。
适合场景:
- 实时日志 / 埋点上传
- 边编码边上传(例如视频编码 + 上传合一)
2. 同步读:NetworkOutputQueue → 流式下载
示例:
export const testNetworkOutputQueue = () => {
// 创建同步读队列对象
const networkOutputQueue = new rcp.NetworkOutputQueue();
try {
const session = rcp.createSession();
// 配置要拉取的数据大小
const numOfChunks = 10;
const chunkLength = 1000;
const totalBytes = numOfChunks * chunkLength;
// 发起 GET 请求,响应数据会暂存在 networkOutputQueue 中
session.get('https://httpbin.org/bytes/' + totalBytes.toString(), networkOutputQueue)
.then((response) => {
if (response && response.statusCode === 200) {
console.info(`get byts succeeded.`);
} else {
console.error(`get byts failed.`);
}
session.close();
}).catch((err: Error) => {
console.error(`get byts error: ${err.message}`);
session.close();
});
// 按需从 networkOutputQueue 中循环读取,每秒读 1000 字节
let totalGetLength = 0;
const intervalId = setInterval(() => {
const chunk = networkOutputQueue.read(chunkLength);
totalGetLength += chunk.byteLength;
console.info(`get byts totalGetLength: ${totalGetLength}`);
if (totalGetLength === totalBytes) {
clearInterval(intervalId);
console.info(`get byts finished.`);
}
}, 1000);
} catch (error) {
console.error(`create session error: ${JSON.stringify(error)}`);
}
}
关键点:
NetworkOutputQueue充当“缓冲池”:- 网络层把响应 body 持续写入队列;
- 你可以在任意时刻调用
read(chunkLength)拿一块数据。
- 示例里每秒读 1000 字节,模拟“按需消费”的业务,比如:
- 分段写入文件;
- 逐步解码音视频数据;
- 流式处理 JSON / 日志 / 二进制协议。
小提示:真实用法里,
read()之后要把数据写入本地文件或交给上层处理,这里只是打印长度示意。
三、基于回调接口的流式传输:uploadFromStream / downloadToStream
上面是“队列模型”——由框架提供 Queue,应用从 Queue 写/读。
下面这一组是“回调接口模型”——你自己实现 ReadStream / WriteStream 接口,RCP 在传输过程中回调你的读/写方法。
1. 上传:实现 ReadStream + uploadFromStream
1)定义 FdReadStream:从文件读数据
import { rcp } from '@kit.RemoteCommunicationKit';
import fs from '@ohos.file.fs';
class FdReadStream implements rcp.ReadStream {
readonly fd: number;
constructor(fd: number) {
this.fd = fd;
}
async read(buffer: ArrayBuffer): Promise<number> {
return fs.read(this.fd, buffer);
}
}
- 实现
rcp.ReadStream接口:- 持有一个文件描述符
fd; - 在
read(buffer)里调用fs.read读数据到buffer里; - 返回实际读取的字节数。
- 持有一个文件描述符
2)调用 uploadFromStream 上传
export function testUploadFromStream(uploadFilePath: string) {
try {
const session = rcp.createSession();
// 打开需要上传的本地文件
const file = fs.openSync(uploadFilePath, fs.OpenMode.READ_ONLY);
// 用 FdReadStream 包装成 RCP 需要的流
const fileStream = new rcp.UploadFromStream(new FdReadStream(file.fd));
session.uploadFromStream('https://httpbin.org/anything', fileStream)
.then((resp) => {
console.info(`testUploadFromStream response: ${JSON.stringify(resp)}`);
if (resp && resp.statusCode === 200) {
console.info(`testUploadFromStream succeeded.`);
} else {
console.error(`testUploadFromStream failed.`);
}
fs.closeSync(file.fd);
session.close();
})
.catch((error: Error) => {
console.error(`testUploadFromStream error: ${JSON.stringify(error)}`);
fs.closeSync(file.fd);
session.close();
});
} catch (error) {
console.error(`testUploadFromStream error: ${JSON.stringify(error)}`);
}
}
- 你只要负责:
- 打开文件;
- 实现“怎么从文件读到 buffer”;
- RCP 会在上传过程中不断回调
read(),把数据流式上传出去。
相比整文件读到内存再上传,流式方式更省内存、适合大文件。
2. 下载:实现 WriteStream + downloadToStream
1)定义 FdWriteStream:写数据到文件
class FdWriteStream implements rcp.WriteStream {
readonly fd: number;
constructor(fd: number) {
this.fd = fd;
}
async write(buffer: ArrayBuffer): Promise<number | void> {
return fs.write(this.fd, buffer);
}
}
- 实现
rcp.WriteStream接口:- 持有文件描述符,调用
fs.write把每一块数据写入到文件。
- 持有文件描述符,调用
2)调用 downloadToStream 把响应体写到流
export function testDownloadToStream(downloadToPath: string) {
try {
const session = rcp.createSession();
// 打开用于保存下载结果的文件(不存在则创建)
const file = fs.openSync(downloadToPath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY);
// 包装成 DownloadToStream
const fileStream = {
kind: 'stream',
stream: new FdWriteStream(file.fd)
} as rcp.DownloadToStream;
session.downloadToStream('https://httpbin.org/bytes/', fileStream)
.then((resp) => {
console.info(`testDownloadToStream response: ${JSON.stringify(resp)}`);
if (resp && resp.statusCode === 200) {
console.info(`testDownloadToStream succeeded.`);
} else {
console.error(`testDownloadToStream failed.`);
}
fs.close(file.fd);
session.close();
})
.catch((error: Error) => {
console.error(`testDownloadToStream error: ${JSON.stringify(error)}`);
fs.close(file.fd);
session.close();
});
} catch (err) {
console.error(`testDownloadToStream error: ${JSON.stringify(err)}`);
}
}
- 每次网络层收一块数据,就会调用
write(buffer); - 你负责把这块数据写到文件中;
- 最终在
downloadToPath这个文件里就有完整的内容。
这个模式非常适合配合之前的 断点续传:
每一块写入文件指定偏移,再控制transferRange去续传。
四、这一节的“考点小抄”
可以直接贴进你的 HarmonyOS 笔记里:
- 流式传输适用场景
- 大文件上传下载
- 实时日志 / 埋点
- 直播、实时数据流
- 两类玩法
- 基于缓冲区队列
NetworkInputQueue:同步写队列 →write()写数据,close()结束;- 搭配
session.post(url, queue)做流式上传。
- 搭配
NetworkOutputQueue:同步读队列 →read(size)读数据;- 搭配
session.get(url, queue)做流式下载。
- 搭配
- 基于回调接口
uploadFromStream(url, UploadFromStream):- 实现
ReadStream.read(buffer),从文件/内存/自定义源读数据;
- 实现
downloadToStream(url, DownloadToStream):- 实现
WriteStream.write(buffer),把数据写到文件/内存/自定义 sink。
- 实现
- 基于缓冲区队列
- 设备支持
- Phone / 2in1 / Tablet / Wearable
- 自 5.1.1(19) 起新增 TV 支持

浙公网安备 33010602011771号