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

React Native文件下载触发RangeError: Property storage超限问题求助

解决React Native接收大Buffer时的RangeError: Property storage exceeds 196607 properties问题

问题背景

开发文件存储应用时,文件以Buffer格式存储在MongoDB中;用户请求文件时,服务器返回对应Buffer数据,但React Native接收响应后抛出上述RangeError,导致应用崩溃或卡顿。

用户代码如下:

downloadFile 代码

import { Helpers } from '#/utils';
import { get } from 'lodash';

const downloadFile = async (fileDetails, isConnected, downloadFileMutation) => {
  const channelId =
    await Helpers.createChannelForProgressNotificationOfUploadingAndDownloadingFiles();
  const notificationId =
    await Helpers.uploadAndDownloadFileProgressNotification(
      'downloadFile',
      0,
      channelId,
      Constants.NOTIFICATION_STATUS.START,
      `Downloading ${fileDetails.name} File`
    );
  downloadFileMutation.mutate(
    {
      "files": [fileDetails._id],
    },
    {
      "onSuccess": async (filesDetails) => {
        console.log('filesDetails', filesDetails);
        await Helpers.cancelNotification(notificationId);
        Helpers.uploadAndDownloadFileProgressNotification(
          'downloadFile',
          1,
          channelId,
          Constants.NOTIFICATION_STATUS.END,
          `${fileDetails.name} downloaded successfully`,
          'click to view file'
        );
        const listOfFileNamesWithBuffer = filesDetails.fileBuffers.map(
          (data) => ({
            "name": data.file.originalname,
            "buffer": data.file.buffer.data,
          })
        );
        await Helpers.downloadAndStoreFile(listOfFileNamesWithBuffer);
      },
      "onError": ({
        "response": {
          "data": { "error" },
        },
      }) => {
        Helpers.toastMessage(
          get(error, 'description', Constants.COMMON_ERROR_MESSAGE),
          'error'
        );
      },
    }
  );
};

export default downloadFile;

downloadFileMutation 代码

import { encryptedLockerAxiosClient } from '#/utils';
import { getFilesApiEndpoint } from '#/config/routes';

const getFile = (fileIds, authToken) =>
  encryptedLockerAxiosClient
    .post(
      getFilesApiEndpoint,
      {
        ...fileIds,
      },
      {
        "headers": {
          "Authorization": authToken,
        },
      }
    )
    .then((data) => ({
      "fileBuffers": data.data,
      "status": data.status,
    }));

const useGetFile = (authToken, progressCallback, options = {}) => {
  const mutation = useMutation((fileIds) => getFile(fileIds, authToken), {
    ...options,
  });

  return mutation;
};
export default useGetFile;

原因分析

该错误是因为React Native底层的V8引擎对单个对象的可枚举属性数量有上限(196607)。当服务器返回的Buffer数据过大时,data.file.buffer.data会被解析成一个长度超过该上限的Uint8Array,触发引擎的属性数量限制检查,导致RangeError。

解决方案

方案1:Base64编码传输(适合中小文件)

通过将Buffer转为Base64字符串传输,避免直接传递超大数组:

服务器端修改

将MongoDB中存储的Buffer转为Base64字符串后返回:

// 假设服务器端使用Node.js/Express
app.post('/api/files', async (req, res) => {
  const files = await File.find({ _id: { $in: req.body.files } });
  const responseData = files.map(file => ({
    file: {
      originalname: file.originalname,
      buffer: file.buffer.toString('base64') // 转Base64
    }
  }));
  res.json(responseData);
});

客户端修改

将接收的Base64字符串转回Uint8Array:

// downloadFile中onSuccess部分修改
const listOfFileNamesWithBuffer = filesDetails.fileBuffers.map((data) => {
  // Base64转Uint8Array
  const base64Str = data.file.buffer;
  const binaryStr = atob(base64Str);
  const buffer = Uint8Array.from(binaryStr, c => c.charCodeAt(0));
  
  return {
    name: data.file.originalname,
    buffer
  };
});
await Helpers.downloadAndStoreFile(listOfFileNamesWithBuffer);

方案2:流式下载到本地(适合大文件)

使用React Native文件系统库直接流式下载文件,避免将整个Buffer加载到内存:

依赖安装

npm install react-native-fs
# 或
yarn add react-native-fs

客户端代码修改

import RNFS from 'react-native-fs';
import { Helpers } from '#/utils';
import { get } from 'lodash';

const downloadFile = async (fileDetails, authToken) => {
  const channelId = await Helpers.createChannelForProgressNotificationOfUploadingAndDownloadingFiles();
  const notificationId = await Helpers.uploadAndDownloadFileProgressNotification(
    'downloadFile',
    0,
    channelId,
    Constants.NOTIFICATION_STATUS.START,
    `Downloading ${fileDetails.name} File`
  );

  // 本地存储路径
  const downloadPath = `${RNFS.DocumentDirectoryPath}/${fileDetails.name}`;
  const downloadOptions = {
    fromUrl: `${API_BASE_URL}/api/files/${fileDetails._id}`, // 改为直接返回二进制文件的接口
    toFile: downloadPath,
    headers: { Authorization: authToken },
    // 进度回调
    progress: (res) => {
      const progress = res.bytesWritten / res.contentLength;
      Helpers.uploadAndDownloadFileProgressNotification(
        'downloadFile',
        progress,
        channelId,
        Constants.NOTIFICATION_STATUS.IN_PROGRESS,
        `Downloading ${fileDetails.name} File`,
        `${Math.round(progress * 100)}%`
      );
    },
    progressDivider: 10 // 每10%更新一次通知
  };

  try {
    await RNFS.downloadFile(downloadOptions).promise;
    await Helpers.cancelNotification(notificationId);
    Helpers.uploadAndDownloadFileProgressNotification(
      'downloadFile',
      1,
      channelId,
      Constants.NOTIFICATION_STATUS.END,
      `${fileDetails.name} downloaded successfully`,
      'click to view file'
    );
    // 后续可记录文件路径到本地数据库
  } catch (error) {
    Helpers.toastMessage(
      get(error, 'message', Constants.COMMON_ERROR_MESSAGE),
      'error'
    );
  }
};

export default downloadFile;

总结

  • 中小文件优先选择Base64编码传输,实现简单且兼容JSON接口;
  • 大文件推荐流式下载到本地文件系统,避免内存溢出,提升性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 06:09:52