如何使用Retrofit通过URI将图片/文件上传至服务器?
使用Retrofit通过文件URI上传图片/文件的完整实现方案
嘿,我看了你给出的上传代码片段,已经走对方向啦!这里帮你补全完整的实现流程,还会提醒你一些容易踩坑的细节,确保你能顺利用Retrofit完成文件上传:
第一步:定义Retrofit上传接口
首先得先写好Retrofit的服务接口,指定Multipart请求和参数:
public interface FileUploadService { @Multipart @POST("upload") // 替换成你的服务器实际上传接口路径 Call<ResponseBody> uploadFile( @Part("description") RequestBody description, // 描述字段,按需调整 @Part MultipartBody.Part filePart // 要上传的文件部分 ); }
第二步:补全上传逻辑代码
你已经写了一部分,这里帮你完善并适配Android新版本的存储规则:
// 假设你已经拿到了文件的Uri和路径 Uri fileUri = ...; // 你的目标文件URI String filePath = ...; // 文件路径 try { // 1. 构建描述字段的RequestBody String descriptionText = "Sample description"; RequestBody description = RequestBody.create( MediaType.parse("multipart/form-data"), descriptionText ); // 2. 正确获取文件的MIME类型 String mimeType = null; // 优先通过ContentResolver获取,比扩展名更准确 if (fileUri != null) { mimeType = getContentResolver().getType(fileUri); } // 如果ContentResolver拿不到,再尝试从扩展名解析 if (mimeType == null) { String fileExtension = MimeTypeMap.getFileExtensionFromUrl(fileUri.toString()); if (fileExtension != null) { mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension.toLowerCase()); } } // 兜底设置默认类型,避免null if (mimeType == null) { mimeType = "application/octet-stream"; } // 3. 构建文件的RequestBody(适配Android 10+分区存储) RequestBody fileRequestBody; File targetFile = new File(filePath); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10及以上,用ContentResolver获取输入流转字节数组,避免直接访问File的权限问题 InputStream inputStream = getContentResolver().openInputStream(fileUri); // 这里可以用Apache的IOUtils,或者自己实现流转字节数组的工具方法 byte[] fileBytes = IOUtils.toByteArray(inputStream); fileRequestBody = RequestBody.create(MediaType.parse(mimeType), fileBytes); } else { // 低版本直接用File对象构建 fileRequestBody = RequestBody.create(MediaType.parse(mimeType), targetFile); } // 4. 创建MultipartBody.Part,指定服务器接收的参数名(比如"file") MultipartBody.Part filePart = MultipartBody.Part.createFormData( "file", targetFile.getName(), // 文件名,可自定义 fileRequestBody ); // 5. 初始化Retrofit并发起请求 Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://your-server-domain.com/") // 替换成你的服务器基础地址 .addConverterFactory(GsonConverterFactory.create()) // 按需添加转换器 .build(); FileUploadService uploadService = retrofit.create(FileUploadService.class); Call<ResponseBody> uploadCall = uploadService.uploadFile(description, filePart); // 推荐用异步调用,避免阻塞主线程 uploadCall.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if (response.isSuccessful()) { // 上传成功,处理服务器返回结果 try { Log.d("UploadSuccess", response.body().string()); } catch (IOException e) { e.printStackTrace(); } } else { // 上传失败,处理错误信息 try { Log.e("UploadFail", response.errorBody().string()); } catch (IOException e) { e.printStackTrace(); } } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { // 网络异常处理 t.printStackTrace(); Log.e("UploadError", "网络请求失败:" + t.getMessage()); } }); } catch (IOException e) { e.printStackTrace(); }
关键注意事项
- 权限适配:Android 10及以上如果用ContentResolver访问文件,无需申请
READ_EXTERNAL_STORAGE;但如果是直接操作File,还是需要申请对应权限。Android 13+要申请READ_MEDIA_IMAGES/READ_MEDIA_VIDEO等细分权限。 - MIME类型准确性:优先用
getContentResolver().getType(uri)获取,避免扩展名错误导致服务器无法识别文件类型。 - Retrofit版本兼容:如果用Retrofit 2.9.0+,部分
RequestBody.create重载会标记过时,建议用上述代码中的方式构建,或者直接用MultipartBody.Part.createFormData简化流程。 - 网络请求线程:绝对不能在主线程执行网络请求,必须用
enqueue异步调用,或者在子线程中用execute同步调用。
内容的提问来源于stack exchange,提问作者Asset Bekbossynov
相关产品推荐
相关产品推荐

