跳到主要内容

实现请求暂停、恢复与断点续传

约束与限制

请求暂停、恢复与断点续传能力支持Phone、2in1、Tablet、Wearable设备。并且从5.1.1(19)开始,新增支持TV设备。

请求暂停、恢复

场景介绍

Remote Communication Kit提供完善的功能支持,包括请求的暂停和恢复功能。这不仅涵盖接收暂停,还包括发送暂停。

使用实例

  1. 导入需要的模块。

    import { rcp } from '@kit.RemoteCommunicationKit';
    import { util } from '@kit.ArkTS';
  2. 定义调试信息接口、调试信息源类型以及调试信息序列化函数,用于将调试信息序列化为StringifiedDebugInfo数组。函数首先根据infoSource的类型获取调试信息,然后使用TextDecoder将调试信息的data字段解码为字符串,并返回一个包含解码后的调试信息的数组。

    const HTTP_SERVER_POST: string = "https://example.org/anything";
    // 定义调试信息接口
    interface StringifiedDebugInfo {
    type: rcp.DebugEvent;
    data: string;
    };
    // 定义调试信息源类型
    type DebugInfoSource = undefined | rcp.DebugInfo[] | rcp.Response;

    // 定义调试信息序列化函数
    function debugInfoStringify(infoSource: DebugInfoSource): StringifiedDebugInfo[] {
    const debugInfo = Array.isArray(infoSource)
    ? (infoSource as rcp.DebugInfo[])
    : (infoSource as rcp.Response).debugInfo;

    if (!debugInfo) {
    return [];
    }

    const decoder = util.TextDecoder.create('utf-8');
    return debugInfo.map((i: rcp.DebugInfo): StringifiedDebugInfo => {
    return {
    type: i.type,
    data: decoder.decodeToString(new Uint8Array(i.data)).trim(),
    };
    });
    }
  3. 获取发送暂停和恢复事件,用于从调试信息中筛选出发送暂停和恢复事件。

    function getSendPausedEvents(debugInfo: DebugInfoSource) {
    return debugInfoStringify(debugInfo).filter((i) => i.data.startsWith('[[RCP]]: Pause sending'));
    }

    function getSendResumedEvents(debugInfo: DebugInfoSource) {
    return debugInfoStringify(debugInfo).filter((i) => i.data.startsWith('[[RCP]]: Resume sending'));
    }
  4. 编写发起请求的函数。

    const SendingPauseByTimeout = async (done: Function): Promise<void> => {
    const session = rcp.createSession();
    const request = new rcp.Request(HTTP_SERVER_POST);
    // 定义发送暂停策略,kind为'timeout',timeoutMs为1ms
    const sendPolicy: rcp.SendingPausePolicy = {
    kind: 'timeout',
    timeoutMs: 1,
    };
    // 定义暂停策略,sending字段引用了上述定义的发送暂停策略
    const pausePolicy: rcp.PausePolicy = {
    sending: sendPolicy,
    };
    // 设置请求的配置,包括传输策略和跟踪信息
    request.configuration = {
    transfer: {
    pausePolicy: pausePolicy,
    },
    tracing: {
    infoToCollect: {
    textual: true,
    },
    },
    };
    // 定义请求体数据
    const data = 'TestData';
    // 设置请求头,'Content-Length'字段表示请求体的长度
    request.headers = {
    'Content-Length': data.length.toString(),
    };
    // 定义布尔型标志变量用于控制请求体生成
    let isReadCompleted = false;
    // 设置请求方法为POST
    request.method = 'POST';
    // 定义请求体内容生成函数,如果read为true,则返回空的ArrayBuffer,否则生成包含请求体数据的ArrayBuffer
    request.content = (maxSize) => {
    if (isReadCompleted) {
    return new ArrayBuffer(0);
    }
    isReadCompleted = true;
    const buffer = new ArrayBuffer(data.length);
    util.TextEncoder.create('utf-8').encodeIntoUint8Array(data, new Uint8Array(buffer));
    return buffer;
    };

    // 发送请求并等待响应
    const response = await session.fetch(request)

    // 从响应的调试信息中获取发送暂停和恢复事件
    const pausedEvents = getSendPausedEvents(response);
    const resumedEvents = getSendResumedEvents(response);

    // 关闭会话
    session.close();
    // 调用完成回调函数
    done();
    }

实现断点续传

场景介绍

在需要接续数据请求的场景中,用户可以通过定义TransferRange对象的from和to属性来控制数据的截取范围。下载的内容可以被准确地截取并拼接到目标文件中,确保数据的完整性和一致性,开发者可以灵活地管理和恢复下载过程。

使用示例

  1. 导入模块。

    import { rcp } from '@kit.RemoteCommunicationKit';
    import { BusinessError } from '@kit.BasicServicesKit';
  2. 创建session,定义请求URL,并对request进行配置,同时定义变量以记录下载文件的总大小。

    // 创建会话
    let session: rcp.Session | null = rcp.createSession();
    // 定义服务器地址
    const kHttpServerAddress = "http://www.example.com/fetch";
    // 创建请求
    const request = new rcp.Request(kHttpServerAddress, "GET");
    // 定义变量记录下载文件的大小
    let totalSize = 0;
    // 定义一个存储上次传输位置的变量
    let lastTransferPosition = 0;
  3. 编写一个函数以获取要下载的文件大小。有多种方法可以获取下载文件的大小,请根据实际需求选择合适的方法。在本例中,通过从响应数据的header中的content-range字段来获取下载文件的总大小。

    /**
    * 获取要下载文件的大小
    *
    * @returns 文件的大小
    */
    async function getTotalSize(): Promise<number> {
    request.transferRange = { from: 0, to: 1 };
    try {
    let rep = await session?.fetch(request);
    if (rep) {
    // 从响应数据的header的content-range字段中提取出文件的大小
    let contentRange = rep.headers['content-range'];
    let sizeStr = contentRange ? contentRange.substring(contentRange.indexOf('\/') + 1, contentRange.length) : '0';
    totalSize = Number(sizeStr);
    }
    } catch (err) {
    console.error(`getTotalSize error code is ${err.code}, error data is ${err.data}`);
    }
    console.info(`getTotalSize totalSize: ${totalSize.toString()}`);
    return totalSize;
    }
  4. 编写一个依据传输范围下载文件的函数。

    /**
    * 根据传输范围下载文件
    *
    * @param from - 传输范围的起始位置
    * @param to - 传输范围的结束位置
    */
    function downloadTransfer(from: number, to: number) {
    // 设置请求的数据传输范围
    request.transferRange = { from: from, to: to };
    session?.fetch(request).then((rep) => {
    if (rep.body) {
    // 处理响应,可以在此处将文件保存到本地
    console.info(`Response succeeded: ${JSON.stringify(rep.headers)}`);
    // 下次传输的起始位置 = 上次的位置 + 本次传输数据的长度
    lastTransferPosition += rep.body.byteLength;
    if (lastTransferPosition < totalSize) {
    // 计算下一次传输范围的结束位置
    const nextTo = Math.min(lastTransferPosition + 100, totalSize);
    // 递归调用继续下载下一段数据
    downloadTransfer(lastTransferPosition, nextTo);
    } else {
    console.info("Response succeeded, completed.");
    }
    }
    }).catch((err: BusinessError) => {
    console.error(`Continue transfer error: code is ${err.code}, message is ${err.message}`);
    });
    }
  5. 使用以下方式开始下载。

    /**
    * 开始下载
    */
    async function startDownload() {
    if (!session) {
    session = rcp.createSession();
    }
    // 传输位置归零
    lastTransferPosition = 0;
    // 获取要下载文件的总大小
    totalSize = await getTotalSize();
    // 计算传输范围的结束位置
    const nextTo = Math.min(lastTransferPosition + 100, totalSize);
    // 开始下载
    downloadTransfer(lastTransferPosition, nextTo);
    }
  6. 使用以下方式暂停下载。

    /**
    * 暂停下载
    */
    function pauseDownload() {
    // 取消下载请求
    session?.cancel(request);
    }
  7. 使用以下方式继续下载。

    /**
    * 继续下载
    */
    function resumeDownload() {
    // 计算传输范围的结束位置
    const nextTo = Math.min(lastTransferPosition + 100, totalSize);
    // 开始下载
    downloadTransfer(lastTransferPosition, nextTo);
    }
  8. 使用以下方式停止下载。

    /**
    * 停止下载
    */
    function stopDownload() {
    // 取消下载请求
    session?.cancel(request);
    // 关闭session
    session?.close();
    session = null;
    }