React Native Axios请求5-8MB大JSON写入Realm报网络错误
React Native Axios拉取大体积JSON接口报unexpected end of stream问题
问题现象
- React Native应用中使用Axios拉取5MB~8MB的大型JSON数据写入Realm数据库,同域名下其他小数据量接口均能正常运行,仅返回5MB级数据的
linksearch3接口触发Network Error报错,错误信息包含unexpected end of stream,请求readyState为4、status为0。 - 已在Axios配置中设置
timeout: 0,maxContentLength、maxBodyLength均为Infinity;使用Postman调用该接口可正常获取响应,全程耗时约30秒。
现有代码
Axios实例配置
import axios from 'axios'; import apiConfig from './apiAuth/config'; import {getAccessToken} from '../services/apiAuth/tokenClient'; const apiClient = axios.create({ baseURL: apiConfig.baseUrl, timeout: 0, maxContentLength: Infinity, maxBodyLength: Infinity, headers: { 'Content-Type': 'application/json', Accept: '*/*', 'Accept-Encoding': 'gzip, deflate, br', Connection: 'keep-alive', }, }); export {apiClient};
Realm数据导入逻辑
requestPrices = async (httpClient) => { let itens = null; await httpClient .get(apiConfig.url_prices) .then((response) => { itens = response.data; return new Promise((resolve) => { if (itens) { const databaseOptions = {allSchemas}; Realm.open(databaseOptions).then((realm) => { realm.write(() => { if (itens) { const resolveItens = itens.prices.map(async (obj, k) => { console.log('inserting price of item = ', k); return await realm.create('Price', obj, true); }); Promise.all(resolveItens).then(() => resolve(true)); } else { console.log('itens: ', itens); } }); }); } else { resolve(true); } }); }) .catch((error) => { console.log(error); Alertmsg.danger('Price: ' + Const.SYNC_DATA_ERROR); });
报错详情
{ [Error: Network Error] config: { url: 'myapi/path/linksearch3?cdColaborador=7442', method: 'get', headers: { Accept: '*/*', 'X-Session-ID': 'my-session-id-1234', 'Accept-Encoding': 'gzip, deflate, br', Connection: 'keep-alive' }, baseURL: 'http://myip.com:8080/', transformRequest: [ [Function: transformRequest] ], transformResponse: [ [Function: transformResponse] ], timeout: 0, adapter: [Function: xhrAdapter], xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: Infinity, maxBodyLength: Infinity, validateStatus: [Function: validateStatus], transitional: { silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false }, data: undefined }, request: { UNSENT: 0, OPENED: 1, HEADERS_RECEIVED: 2, LOADING: 3, DONE: 4, readyState: 4, status: 0, timeout: 0, withCredentials: true, upload: {}, _aborted: false, _hasError: true, _method: 'GET', _perfKey: 'network_XMLHttpRequest_http://myip.com:8080/myapi/path/linksearch3?cdColaborador=7442', _response: 'unexpected end of stream', _url: 'http://myip.com:8080/myapi/path/linksearch3?cdColaborador=7442', _timedOut: false, _trackingName: 'unknown', _incrementalEvents: false, _performanceLogger: { _timespans: { 'network_XMLHttpRequest_http://myip.com:8080/rest-api/auth': { startTime: 8630575.8016, startExtras: undefined, endExtras: undefined, endTime: 8630951.7208, totalTime: 375.91919999942183 }, 'network_XMLHttpRequest_http://myip.com:8080/path/linkserach1': { startTime: 8631528.126, startExtras: undefined, endExtras: undefined, endTime: 8631973.0137, totalTime: 444.8876999989152 }, 'network_XMLHttpRequest_http://myip.com:8080/path/linksearch2': { startTime: 8632033.7623, startExtras: undefined, endExtras: undefined, endTime: 8632421.9302, totalTime: 388.16789999976754 }, 'network_XMLHttpRequest_http://myip.com:8080/path/linksearch3': { startTime: 8632478.1932, startExtras: undefined, endExtras: undefined, endTime: 8656735.1845, totalTime: 24256.991299999878 }, 'network_XMLHttpRequest_http://myip.com:8080/path/linksearch4': { startTime: 8660848.4174, startExtras: undefined, endExtras: undefined, endTime: 8661277.8728, totalTime: 429.455399999395 }, 'network_XMLHttpRequest_http://myip.com:8080/path/linksearch5': { startTime: 8661367.8179, startExtras: undefined, endExtras: undefined, endTime: 8662393.9181, totalTime: 1026.1001999992877 }, 'network_XMLHttpRequest_http://myip.com:8080/myapi/path/linksearch3?cdColaborador=7442': { startTime: 8662680.029, startExtras: undefined } }, _extras: {}, _points: { initializeCore_start: 64197.2119, initializeCore_end: 64461.4723 }, _pointExtras: {}, _closed: false }, responseHeaders: undefined, _requestId: null, _cachedResponse: undefined, _headers: { accept: '*/*', 'x-session-id': 'my-session-id-1234', 'accept-encoding': 'gzip, deflate, br', connection: 'keep-alive' }, _responseType: '',
可行解决方案
- 修改Accept-Encoding请求头
React Native的XHR网络层对Brotli(br)压缩格式的大体积响应解压存在兼容问题,大文件解压过程中容易出现流中断。直接删除Axios配置中自定义的Accept-Encoding头,让系统自动选择支持的编码格式,或仅保留Accept-Encoding: 'gzip'即可。 - 修复Realm写入逻辑缺陷
当前代码在realm.write同步事务块内使用async/await+Promise.all包裹同步的realm.create操作,会导致Promise链断裂、事务提前退出,额外抬升内存峰值,间接挤压网络请求的内存空间。realm.create本身是同步API,不需要加异步包裹,所有写入操作直接在realm.write块内同步执行即可,事务会在所有同步操作完成后自动提交。 - 调整移动端原生网络超时配置
从报错日志可见失败请求在24秒左右中断,和Android系统默认的25秒网络空闲超时阈值吻合,Postman调用耗时30秒刚好超过该阈值。Android端需要修改OkHttp客户端的读、写、连接超时到60秒以上,同时在网络安全配置中关闭不必要的流量拦截。 - 替换请求实现方式
Axios在React Native中默认使用的xhrAdapter对大体积响应的处理存在内存上限问题,可直接使用原生fetchAPI测试该接口,fetch基于原生网络层实现,大文件传输稳定性高于XHR;也可切换为基于原生网络库封装的请求客户端。 - 优化接口拉取逻辑
58MB的单接口响应本身不符合移动端接口设计规范,优先协调后端支持分页拉取,每次拉取5001000条数据分批写入Realm,既可以避免流中断问题,也能降低应用内存峰值,减少同步过程中的UI卡顿。
内容的提问来源于stack exchange,提问作者rodolfo
相关产品推荐
相关产品推荐

