OKHTTP传输92MB附件请求耗时过长问题求助
OkHttp 4.10.0 大文件上传耗时过长问题排查
我在Android应用中使用OkHttp 4.10.0向后端发送请求,当上传92MB的文件时,从执行client.newCall(request).execute()到实际发送请求需要约1分钟。怀疑和内部缓存或压缩有关,附上核心代码:
HttpUrl httpUrl = new HttpUrl.Builder() .scheme(BackendEndpointConstants.PROTOCOL) .host(BackendEndpointConstants.HOST) .port(Integer.valueOf(BackendEndpointConstants.PORT)) .addPathSegment(BackendEndpointConstants.BASE_ENDPOINT) .addPathSegment(BackendEndpointConstants.BASE_ENDPOINT_VERSION) .addPathSegment(BackendEndpointConstants.ATTACHMENTS) .build(); MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder(); multipartBodyBuilder.addFormDataPart("fileDescription", JSONUtils.toString(uploadFileDescriptor)); File fileAttachment = basePath.resolve(fileAtachmentDTO.getPathToFile()).toFile(); multipartBodyBuilder.setType(MultipartBody.FORM); multipartBodyBuilder.addFormDataPart("files[]", fileAttachment.getName(), RequestBody.create(fileAttachment, MediaType.parse(fileAttachmentDTO.getMimeType()))); MultipartBody multipartBody = multipartBodyBuilder.build(); Request request = new Request.Builder() .addHeader("Authorization", "Bearer " + userApiToken) .url(httpUrl) .post(multipartBody) .build(); OkHttpClient client = new OkHttpClient.Builder().writeTimeout(10, TimeUnit.MINUTES).readTimeout(1, TimeUnit.MINUTES).build(); try (okhttp3.Response response = client.newCall(request).execute()) { String responseBody = response.body().string(); } catch (IOException e) { Log.e(TAG, "Error", e); throw new RuntimeException(e); }
可能的原因及优化方案
1. 未启用分块传输,OkHttp预加载整个文件到内存
默认情况下,OkHttp的MultipartBody会把整个文件读入内存后再发送,大文件会导致内存占用过高且准备时间拉长。
优化方案:
改用流式RequestBody,让OkHttp以分块(chunked)方式上传:
// 替换原有的RequestBody.create方式 RequestBody fileBody = RequestBody.create( new FileInputStream(fileAttachment), MediaType.parse(fileAttachmentDTO.getMimeType()) ); multipartBodyBuilder.addFormDataPart("files[]", fileAttachment.getName(), fileBody);
2. 未启用请求体压缩
如果文件是可压缩类型(如文本、PNG图片),启用gzip压缩能大幅减小传输体积,降低耗时。
优化方案:
给OkHttpClient添加压缩拦截器,自动对请求体进行gzip压缩:
OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(chain -> { Request originalRequest = chain.request(); if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) { return chain.proceed(originalRequest); } Request compressedRequest = originalRequest.newBuilder() .header("Content-Encoding", "gzip") .method(originalRequest.method(), gzip(originalRequest.body())) .build(); return chain.proceed(compressedRequest); }) .writeTimeout(10, TimeUnit.MINUTES) .readTimeout(1, TimeUnit.MINUTES) .build(); // 辅助压缩方法 private RequestBody gzip(final RequestBody body) { return new RequestBody() { @Override public MediaType contentType() { return body.contentType(); } @Override public long contentLength() throws IOException { return -1; // 无法提前获取压缩后长度,自动启用分块传输 } @Override public void writeTo(BufferedSink sink) throws IOException { BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); body.writeTo(gzipSink); gzipSink.close(); } }; }
3. 默认缓存机制干扰
请求的缓存逻辑可能导致额外的准备耗时,可以直接禁用该请求的缓存:
优化方案:
在Request中添加缓存控制头部:
Request request = new Request.Builder() .addHeader("Authorization", "Bearer " + userApiToken) .addHeader("Cache-Control", "no-cache") .url(httpUrl) .post(multipartBody) .build();
4. 网络与后端配置问题
- 检查设备网络带宽:92MB文件在正常4G网络下理论传输时间约10-20秒,若耗时远超,可能是网络波动或运营商限速。
- 确认后端支持分块传输:如果后端未配置
Transfer-Encoding: chunked支持,OkHttp会等待完整请求体构造完成后再发送,拉长准备时间。
内容的提问来源于stack exchange,提问作者FranKs
相关产品推荐
相关产品推荐

