You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native中限制下载x字节文件(不使用react-native-fetch-blob)

问题

需要在React Native中实现从URL下载文件的功能,且限制最多仅下载指定字节数(x字节)。原本可通过支持文件流的react-native-fetch-blob实现,但当前场景无法使用该库,希望找到替代方案(优先不依赖第三方库)。

在Web JavaScript中可通过以下代码实现:

async function download(url, maxBytes) {
  return new Promise(async (resolve, reject) => {
      try {
        const response = await fetch(url);
    
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
    
        const reader = response.body.getReader();
        let bytesRead = 0;
        let chunks = [];
        while (bytesRead < maxBytes) {
          const { done, value } = await reader.read();
          if (done) { break; }
          chunks.push(value);
          bytesRead += value.length;
        }
        reader.cancel("Download size limit reached.");
    
        const result = new Uint8Array(bytesRead);
        let offset = 0;
    
        for (const chunk of chunks) {
          result.set(chunk, offset);
          offset += chunk.length;
        }
    
        let str = (new TextDecoder().decode(result));
        // stuff to do with the str here
        return resolve(str);
      } catch (error) {
        console.error('Error downloading the file:', error);
        reject();
      }
  });
}

但React Native的Fetch API不提供ReadableStream,导致上述代码无法运行,需要找到React Native中的可行实现方式。


解决方案

方案一:使用React Native内置XMLHttpRequest(无第三方依赖)

React Native的XMLHttpRequest支持arraybuffer类型的响应,且可通过监听readyState变化,在数据加载过程中控制下载字节数,达到上限时终止请求。

async function downloadWithLimit(url, maxBytes) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'arraybuffer';

    let totalBytesRead = 0;
    let collectedChunks = [];
    let hasResolved = false;

    xhr.onreadystatechange = () => {
      if (hasResolved) return;
      // 当readyState为3时,响应正在加载,可读取已接收的数据
      if (xhr.readyState === XMLHttpRequest.LOADING) {
        try {
          const currentChunk = new Uint8Array(xhr.response);
          const chunkLength = currentChunk.length;

          // 计算剩余可接收字节数
          const remainingBytes = maxBytes - totalBytesRead;
          if (totalBytesRead + chunkLength >= maxBytes) {
            // 截取不超过上限的部分
            const limitedChunk = currentChunk.slice(0, remainingBytes);
            collectedChunks.push(limitedChunk);
            totalBytesRead += remainingBytes;

            // 终止请求
            xhr.abort();
            // 合并所有chunk并解码
            const resultBuffer = new Uint8Array(totalBytesRead);
            let offset = 0;
            collectedChunks.forEach(chunk => {
              resultBuffer.set(chunk, offset);
              offset += chunk.length;
            });
            const decodedStr = new TextDecoder().decode(resultBuffer);
            hasResolved = true;
            resolve(decodedStr);
          } else {
            collectedChunks.push(currentChunk);
            totalBytesRead += chunkLength;
          }
        } catch (err) {
          reject(err);
        }
      }
    };

    xhr.onload = () => {
      if (hasResolved) return;
      if (xhr.status >= 200 && xhr.status < 300) {
        // 下载完成且未超过限制,直接处理全部响应
        const fullBuffer = new Uint8Array(xhr.response);
        const decodedStr = new TextDecoder().decode(fullBuffer);
        resolve(decodedStr);
      } else {
        reject(new Error(`请求失败,状态码:${xhr.status}`));
      }
    };

    xhr.onerror = () => {
      if (!hasResolved) {
        reject(new Error('网络请求出错'));
      }
    };

    xhr.send();
  });
}

说明:

  • 利用XMLHttpRequest.LOADING状态(readyState=3)实时读取已接收的二进制数据
  • 累计字节数达到上限时,截取剩余允许的字节后终止请求
  • 合并所有已接收的chunk并通过TextDecoder解码为字符串

方案二:使用react-native-fs(第三方库备选)

如果内置方案存在兼容性问题,可使用轻量第三方库react-native-fs实现,它支持下载进度监听和中途终止下载。

步骤1:安装依赖

npm install react-native-fs --save
# React Native 0.60+版本自动完成链接,无需额外操作

实现代码

import RNFS from 'react-native-fs';

async function downloadWithRNFS(url, maxBytes) {
  const tempFilePath = `${RNFS.TemporaryDirectoryPath}/temp_download_file`;
  let downloadJobId = null;
  let totalBytesWritten = 0;

  try {
    const downloadPromise = RNFS.downloadFile({
      fromUrl: url,
      toFile: tempFilePath,
      progress: (progressData) => {
        totalBytesWritten = progressData.bytesWritten;
        // 达到字节上限时停止下载
        if (totalBytesWritten >= maxBytes && downloadJobId) {
          RNFS.stopDownload(downloadJobId);
        }
      },
      progressDivider: 1, // 每次接收数据都触发进度回调
    });

    downloadJobId = downloadPromise.jobId;
    await downloadPromise.promise;

    // 读取已下载的文件,截取指定字节数
    const base64Data = await RNFS.read(tempFilePath, maxBytes, 0, 'base64');
    // 将base64转为Uint8Array
    const uint8Array = Uint8Array.from(atob(base64Data), char => char.charCodeAt(0));
    const decodedStr = new TextDecoder().decode(uint8Array);

    // 删除临时文件
    await RNFS.unlink(tempFilePath);
    return decodedStr;
  } catch (error) {
    // 处理主动停止下载的情况
    if (error.message.includes('stopDownload') && totalBytesWritten > 0) {
      const base64Data = await RNFS.read(tempFilePath, maxBytes, 0, 'base64');
      const uint8Array = Uint8Array.from(atob(base64Data), char => char.charCodeAt(0));
      const decodedStr = new TextDecoder().decode(uint8Array);
      await RNFS.unlink(tempFilePath);
      return decodedStr;
    }
    // 删除临时文件后抛出错误
    await RNFS.unlink(tempFilePath).catch(() => {});
    throw new Error(`下载失败:${error.message}`);
  }
}

说明:

  • 通过临时文件存储下载内容,达到字节上限时调用stopDownload终止任务
  • 读取临时文件的前maxBytes字节,转换为字符串后清理临时文件

内容的提问来源于stack exchange,提问作者fres

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 21:54:55