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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 03:55:12