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

Android Java中使用Retrofit通过Model类添加文件上传字段实现WebService数据插入的问题求助

解决Retrofit上传文件+表单字段的问题

看起来你遇到的核心问题是:原来的接口用@Body将Model序列化为JSON发送,但File对象无法被JSON序列化,所以直接在Model里加File字段是行不通的。上传文件需要使用Multipart/form-data格式的请求,而不是JSON请求。下面是一步步的解决方案:

1. 修改Retrofit Service接口

首先要把原来的@POST + @Body改成@Multipart + @Part,每个表单字段(包括文件)单独作为@Part参数传递:

public interface swabtestService {
    @GET("hasil-antigen-list")
    Call<List<SwabtestModel>> getUsers();

    // 改成Multipart请求,每个字段用@Part声明
    @Multipart
    @POST("insert-hantigen")
    Call<SwabtestModel> addswab(
            @Part("hasil") RequestBody hasil,
            @Part("tanggal") RequestBody tanggal,
            @Part("tempat") RequestBody tempat,
            @Part("user_id") RequestBody userId,
            @Part MultipartBody.Part file
    );
}

注意:@Part里的字符串(比如"hasil")要和服务器端接收的参数名完全一致,包括"file_name"也要对应服务器的文件参数名。

2. 调整SwabtestModel(可选)

你可以保留原来的Model类,但可以移除file_name字段(因为文件会单独用MultipartBody.Part传递),或者保留它用于本地逻辑,上传时不用它即可。

3. 修改Activity中的save方法

现在需要把Model里的每个字段转换成RequestBody,并单独处理文件部分:

public void save(SwabtestModel sw){
    // 将普通字符串/数字字段转为RequestBody
    RequestBody hasilBody = RequestBody.create(MediaType.parse("text/plain"), sw.gethasil());
    RequestBody tempatBody = RequestBody.create(MediaType.parse("text/plain"), sw.gettempat());
    RequestBody userIdBody = RequestBody.create(MediaType.parse("text/plain"), sw.getuserid().toString());
    
    // 处理日期:将Date转为服务器预期的字符串格式(这里用你原来的dd/MM/yyyy)
    SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy");
    String tanggalStr = dateFormatter.format(sw.gettanggal());
    RequestBody tanggalBody = RequestBody.create(MediaType.parse("text/plain"), tanggalStr);

    // 处理文件:从Uri创建RequestBody(注意:不要直接依赖filePath,有些Uri无法转为本地文件路径)
    RequestBody fileRequestBody = null;
    try {
        // 获取文件的MIME类型
        String mimeType = getContentResolver().getType(uri);
        // 用InputStream读取文件内容转为字节数组(兼容所有Uri类型)
        byte[] fileBytes = getBytesFromUri(uri);
        fileRequestBody = RequestBody.create(MediaType.parse(mimeType), fileBytes);
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "文件读取失败", Toast.LENGTH_SHORT).show();
        return;
    }

    // 将文件封装为MultipartBody.Part,参数名要和服务器一致(这里是"file_name")
    String fileName = uri.getLastPathSegment();
    MultipartBody.Part filePart = MultipartBody.Part.createFormData("file_name", fileName, fileRequestBody);

    // 调用接口
    Call<SwabtestModel> call = swabtestService.addswab(hasilBody, tanggalBody, tempatBody, userIdBody, filePart);
    call.enqueue(new Callback<SwabtestModel>() {
        @Override
        public void onResponse(Call<SwabtestModel> call, Response<SwabtestModel> response) {
            if(response.isSuccessful()){
                String status = response.body().getStatus();
                Toast.makeText(SwabtestActivity.this, status, Toast.LENGTH_LONG).show();
            } else {
                // 打印错误信息方便调试
                try {
                    Log.e("UPLOAD ERROR", response.errorBody().string());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onFailure(Call<SwabtestModel> call, Throwable t) {
            Log.e("ERROR: ", t.getMessage());
        }
    });
}

// 辅助方法:从Uri读取文件字节数组
private byte[] getBytesFromUri(Uri uri) throws IOException {
    InputStream inputStream = getContentResolver().openInputStream(uri);
    ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
    int bufferSize = 1024;
    byte[] buffer = new byte[bufferSize];
    int len;
    while ((len = inputStream.read(buffer)) != -1) {
        byteBuffer.write(buffer, 0, len);
    }
    inputStream.close();
    return byteBuffer.toByteArray();
}

4. 注意事项

  • 确保服务器端接口支持Multipart/form-data请求,并且参数名和你@Part里的完全匹配;
  • 日期格式要和服务器预期一致,如果服务器用yyyy-MM-dd,就修改你的SimpleDateFormat;
  • 文件的MIME类型尽量准确,比如图片用image/jpeg,文档用application/pdf等,用getContentResolver().getType(uri)可以自动获取正确类型;
  • 如果你选择直接用File对象(仅适用于本地文件Uri),可以替换文件部分的代码:
    File file = new File(filePath);
    RequestBody fileRequestBody = RequestBody.create(MediaType.parse(getContentResolver().getType(uri)), file);
    MultipartBody.Part filePart = MultipartBody.Part.createFormData("file_name", file.getName(), fileRequestBody);
    
    但这种方式不适用于ContentProvider类型的Uri(比如从相册选的图片Uri),所以推荐用字节数组的方式。

内容的提问来源于stack exchange,提问作者andreaniwpi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 03:39:04