Android真机发送30-50KB图片报错,模拟器小文件正常求助
问题排查与解决方案
核心问题分析
报错java.io.IOException: unexpected end of stream本质是客户端与服务器的HTTP请求传输不完整,结合你的代码和场景,核心原因及潜在问题如下:
1. 手动设置的Content-Type头与MultipartBody自动生成的boundary冲突
你手动添加了:
.addHeader("Content-Type", "multipart/form-data; boundary=something")
但MultipartBody.Builder构建请求体时,会自动生成唯一的boundary并附带正确的Content-Type头。手动设置的固定boundary会覆盖自动生成的,导致服务器无法正确解析多部分表单数据——小文件因数据量小可能偶然解析成功,大文件则必然触发解析失败,进而导致连接中断。
2. 其他潜在问题
- PNG压缩参数无效:
Bitmap.CompressFormat.PNG是无损格式,compress方法的第二个质量参数(设为10)完全不生效,反而可能造成不必要的内存占用。 - 不必要的
Connection: close头:强制关闭连接会破坏OkHttp的连接池复用机制,增加连接不稳定的概率。 - LoggingInterceptor日志冗余:当前代码打印的参数存在冗余,虽不会直接引发问题,但后续若扩展读取请求体内容,需注意避免消耗请求流。
修复后的代码
以下是标注修改点的完整修复代码:
public Boolean SendImage_ms(String serverURL) { try { Bitmap image = photo_img_ms; String id_ms = ID_Item.toString(); String filename = "image.png"; ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 修复:PNG格式质量参数无效,若需压缩可改用JPEG image.compress(Bitmap.CompressFormat.PNG, 100, baos); // 若需控制文件大小,可改用JPEG压缩(根据需求选择): // image.compress(Bitmap.CompressFormat.JPEG, 80, baos); // filename = "image.jpg"; RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM) .addFormDataPart("file", filename, RequestBody.create(MEDIA_TYPE_PNG, baos.toByteArray())) .addFormDataPart("id", id_ms) .addFormDataPart("api_key", api_key) .build(); Request request = new Request.Builder() .url(serverURL) // 移除:手动设置的Content-Type头,由MultipartBody自动生成正确的头信息 // .addHeader("Content-Type", "multipart/form-data; boundary=something") // 可选移除:Connection: close,让OkHttp自动管理连接池 // .addHeader("Connection", "close") .method("POST", body) .build(); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new LoggingInterceptor()) .writeTimeout(600, TimeUnit.SECONDS) .readTimeout(600, TimeUnit.SECONDS) .retryOnConnectionFailure(true) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NonNull Call call, @NonNull IOException e) { System.out.println(e.toString()); } @Override public void onResponse(final Call call, final Response response) throws IOException { if (!response.isSuccessful()) { System.out.println("error"); } // 注意:response.body().string()只能调用一次,调用后流会自动关闭 System.out.println(response.body().string()); } }); return true; } catch (Exception ex) { System.out.println(ex.toString()); } return false; } class LoggingInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); long t1 = System.nanoTime(); // 优化日志:移除冗余参数,避免不必要的信息打印 Log.d("OkHttp", String.format("Sending request %s on %s%nHeaders: %s", request.url(), chain.connection(), request.headers())); Response response = chain.proceed(request); long t2 = System.nanoTime(); Log.d("OkHttp", String.format("Received response for %s in %.1fms%nHeaders: %s", response.request().url(), (t2 - t1) / 1e6d, response.headers())); return response; } }
额外建议
- 抓包验证:使用Charles等工具对比模拟器和真机的请求头,确认修复后的Content-Type头包含正确的自动生成boundary。
- 服务器配置检查:确认服务器端的上传文件大小限制是否允许30-50KB的文件。
- 响应体处理:
response.body().string()只能调用一次,若需多次使用需先将内容保存为字符串变量。
内容的提问来源于stack exchange,提问作者zibit zibit
相关产品推荐
相关产品推荐

