Android使用Retrofit2上传MP3音频文件无响应,求助排查
排查Retrofit2上传MP3无响应问题
嘿,我来帮你一步步排查这个Retrofit2上传MP3无响应的问题!从你贴的代码和截图来看,有几个常见的坑可能导致请求卡住或者没反馈,咱们逐个解决:
1. 接口参数格式错误
你的接口里直接用@Part("api_token") String api_token这种写法是有问题的——Retrofit的@Part注解对于非文件类型的参数,需要封装成RequestBody(或者用@PartMap批量处理),不然Retrofit无法正确生成Multipart请求的表单字段。
修改后的接口示例
@Multipart @POST("comments") Call<Wallet> setComment( @Part("voice") RequestBody song, @Part("api_token") RequestBody apiToken, @Part("product") RequestBody productId, @Part("body") RequestBody body ); // 或者用@PartMap更简洁(推荐): @Multipart @POST("comments") Call<Wallet> setComment( @Part("voice") RequestBody song, @PartMap Map<String, RequestBody> params );
2. 文件MediaType不够精确
你用"audio/*"作为文件的MediaType,部分服务器可能无法识别具体的音频格式,导致拒绝处理请求。MP3文件应该明确指定为"audio/mpeg"。
3. 文件路径与权限问题
- Android版本适配:
Environment.getExternalStorageDirectory()在Android Q(API 29)及以上已经废弃,直接用这个路径可能无法访问文件,导致请求体为空。建议改用应用私有外部存储路径。 - 动态权限:Android 6.0+需要动态申请
READ_EXTERNAL_STORAGE权限,没有权限的话根本读不到文件,请求自然发不出去。
4. 回调未添加日志排查
你的onResponse和onFailure只调用了关闭弹窗的方法,完全没有打印任何日志,根本不知道请求是成功、失败还是卡在了某个环节。一定要添加日志输出,才能定位问题。
修改后的完整调用代码
// 第一步:检查并申请动态权限 if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions((Activity) getContext(), new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 100); return; } // 第二步:适配Android版本的文件路径 File audioFile; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // 应用私有外部存储,无需额外权限 audioFile = new File(getContext().getExternalFilesDir(Environment.DIRECTORY_MUSIC), "NAMEAudioRecording.mp3"); } else { audioFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "NAME" + "AudioRecording.mp3"); } // 第三步:检查文件是否存在且可读 if (!audioFile.exists() || !audioFile.canRead()) { Log.e("UploadError", "音频文件不存在或无法读取"); appStore.CloseDilag(); return; } // 第四步:创建正确的RequestBody // 音频文件用精确的MediaType RequestBody requestFile = RequestBody.create(MediaType.parse("audio/mpeg"), audioFile); // 非文件参数封装为RequestBody RequestBody apiToken = RequestBody.create(MediaType.parse("text/plain"), new AppStore(getContext()).LoadMyShereKES(AppStore.Keys_TOKEN)); RequestBody product = RequestBody.create(MediaType.parse("text/plain"), AppStore.ID_BACHGROUD); RequestBody body = RequestBody.create(MediaType.parse("text/plain"), Ediet.getText().toString()); // 第五步:发起请求并添加日志 MyInterFace myInterFace = MyServices.createService(MyInterFace.class); Call<Wallet> calling = myInterFace.setComment(requestFile, apiToken, product, body); calling.enqueue(new Callback<Wallet>() { @Override public void onResponse(Call<Wallet> call, Response<Wallet> response) { appStore.CloseDilag(); // 打印状态码和响应内容 Log.d("RetrofitInfo", "请求状态码:" + response.code()); if (response.isSuccessful()) { Log.d("UploadSuccess", "服务器返回数据:" + response.body().toString()); } else { // 打印错误响应的具体内容(非常重要) try { Log.e("UploadError", "错误响应:" + response.errorBody().string()); } catch (IOException e) { e.printStackTrace(); } } } @Override public void onFailure(Call<Wallet> call, Throwable t) { appStore.CloseDilag(); // 打印失败原因 Log.e("UploadFailure", "请求失败:" + t.getMessage()); t.printStackTrace(); } });
额外排查建议
- 添加Retrofit日志拦截器:在Retrofit初始化时添加
HttpLoggingInterceptor,可以看到完整的请求和响应详情,方便定位问题:
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging) .build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(你的服务器地址) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build();
- 抓包验证请求:用Charles或Fiddler抓包,确认请求是否真的发送到了服务器,以及请求体的参数是否正确。
- 检查服务器配置:确认服务器端的接口支持
Multipart/form-data请求,且参数名(比如voice、api_token)和客户端完全一致。
内容的提问来源于stack exchange,提问作者Doreh Amin
相关产品推荐
相关产品推荐

