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

React Native安卓后台断点续传偶发Chunk大小不符400错误求解

问题根因
  • IOSBackgroundTask配置仅对iOS生效,安卓端没有对应的后台保活配置,切后台/锁屏时系统会限制应用网络权限、回收闲置内存,甚至直接中断上传线程,导致请求体发送不完整,这是报错的核心诱因。你重试同一Chunk持续失败,是因为被系统回收的Chunk内存没有被主动刷新,重试时依然发送残缺内容,重启上传流程后会重新读取完整Chunk,因此可恢复。
  • HTTP头配置冲突:HTTP协议中Content-Length和Transfer-Encoding: Chunked为互斥字段,安卓底层的OkHttp遇到冲突头时处理逻辑异常,后台运行时会忽略Content-Length按分块传输处理,和服务器校验规则不匹配。
  • 全量读取Base64格式Chunk到内存的方式风险高,安卓后台内存紧张时会主动回收大字符串内存,导致实际发送的内容长度低于预期。
  • Content-Length写死为CHUNK_SIZE,若当前是文件最后一个Chunk,实际长度会小于CHUNK_SIZE,和Content-Range的end字段不匹配。
安卓端后台上传必需的额外配置

首先在AndroidManifest.xml中添加必需权限:

<!-- 前台服务权限(安卓10+后台长任务必需) -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 唤醒锁,防止锁屏后CPU休眠中断上传 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- 申请忽略电池优化权限,避免系统查杀上传进程 -->
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

上传前动态申请忽略电池优化权限:

import { Platform } from 'react-native';
if (Platform.OS === 'android') {
  const isIgnored = await RNFetchBlob.isIgnoringBatteryOptimizations();
  if (!isIgnored) {
    await RNFetchBlob.requestIgnoreBatteryOptimizations();
  }
}
代码修复方案
  1. 移除冲突的HTTP头,删除'Transfer-Encoding': 'Chunked'字段,固定大小的Chunk上传使用Content-Length即可,兼容性更好。
  2. 不要把Chunk全量读取到内存,直接用RNFetchBlob提供的文件偏移读取方法,避免内存回收导致内容残缺,修改后的上传逻辑如下:
function uploadChunk(offset) {
  let nextOffset = offset;
  const thisRecordingInProgress = isInProgress();
  const totalBytes = thisRecordingInProgress ? '*' : size;

  if (nextOffset === undefined) {
    const res = await RNFetchBlob.fetch('PUT', url, {
      'Content-Length': '0',
      'Content-Range': `bytes */${totalBytes}`,
    });
    ({ nextOffset } = parseResponse(res));
  }
  // 直接计算当前Chunk的起止位置,不需要预读内容
  const expectChunkLength = Math.min(CHUNK_SIZE, size - nextOffset);
  const end = nextOffset + expectChunkLength - 1;
  const resp = await RNFetchBlob.config({
    IOSBackgroundTask: true,
    // 添加安卓后台通知配置,让系统把上传进程识别为前台服务
    android: {
      notification: {
        channelId: 'resumable_upload',
        channelName: '文件上传服务',
        enableVibrate: false,
        enableLights: false
      }
    }
  })
  .fetch(
    'PUT',
    URL,
    {
      // 用实际Chunk长度,不要写死CHUNK_SIZE
      'Content-Length': `${expectChunkLength}`,
      'Content-Range': `bytes ${nextOffset}-${end}/${totalBytes}`,
      'Content-Type': 'application/octet-stream',
    },
    // 直接读取文件指定偏移范围,不需要加载到内存
    RNFetchBlob.wrap(path, nextOffset, end)
  )
  .uploadProgress(written => {
    // 进度跟踪逻辑
  });

  ({ nextOffset } = await parseResponse(resp));
  if (nextOffset < 0) {
    // 上传完成逻辑
  } else {
    return uploadChunk(nextOffset);
  }
}
  1. 如果必须保留读取Base64 Chunk的逻辑,每次重试Chunk前要重新调用RNFS.read读取内容,不要复用之前读取的Chunk变量,避免使用被系统回收的残缺内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 20:45:00