Retrofit 2.0 超能实践(三),轻松实现多文件/图片上传/Json字符串/表单

通过前两篇姿势的入门

本文出自:http://blog.csdn.net/sk719887916/article/details/51755427 码小白


通过对Retrofit2.0的前两篇的基础入门和案例实践,掌握了怎么样使用Retrofit访问网络,加入自定义header头,包括加入SSL证书,基本的调试基础,cookie同步问题,但很多场景需求是需要文件的上传的,今天主题就来分享怎么用Retrofit2.0 上传文件和图片,表单,包括上传Json等。


使用 Retrofit1.x上传文件

大家都知道在2.0以前版本上传图片的姿势

 public interface ApiManager {
    @Multipart
    @POST("/user/addCarInfo")
    void addCarInfo(@QueryMap Map<String, Object> options, @Part("file") TypedFile file, Callback<JsonElement> response);

}

使用 Retrofit 2.X 上传

Retrofit 2.X上传文件

使用2.0,我们发现以前的TypedFile类型被私有化了 ,无法继续使用1.9的传方式,因此2.x提供了上传方案,可以MultipartBody.Part代替。

 public interface FileUploadService {  
 @Multipart
 @POST("upload")
 Call<ResponseBody> upload(@Part("description") RequestBody description,
                          @Part MultipartBody.Part file);
}

具体用法

先看一个基本的用法:

// 创建 RequestBody,用于封装构建RequestBody
RequestBody requestFile =
        RequestBody.create(MediaType.parse("multipart/form-data"), file);

// MultipartBody.Part  和后端约定好Key,这里的partName是用image
MultipartBody.Part body =
        MultipartBody.Part.createFormData("image", file.getName(), requestFile);

// 添加描述
String descriptionString = "hello, 这是文件描述";
RequestBody description =
        RequestBody.create(
                MediaType.parse("multipart/form-data"), descriptionString);

// 执行请求
Call<ResponseBody> call = service.upload(description, body);
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call,
                           Response<ResponseBody> response) {
        Log.v("Upload", "success");
    }

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

上报一张图片

@Multipart
 @POST("you methd url upload/")
Call<ResponseBody> uploadFile(
        @Part("avatar\\\\\\\\\\\\\\\\"; filename=\\\\\\\\\\\\\\\\"avatar.jpg") RequestBody file);

上报数量确定的多张图片

@POST("upload/")
Call<ResponseBody> uploadFiles(@Part("filename") String description,
                                     @Part("pic\\\\\\\\\\\\\\\\"; filename=\\\\\\\\\\\\\\\\"image1.png") RequestBody imgs1,
                                     @Part("pic\\\\\\\\\\\\\\\\"; filename=\\\\\\\\\\\\\\\\"image2.png") RequestBody imgs2,
                                     @Part("pic\\\\\\\\\\\\\\\\"; filename=\\\\\\\\\\\\\\\\"image3.png") RequestBody imgs3,
                                     @Part("pic\\\\\\\\\\\\\\\\"; filename=\\\\\\\\\\\\\\\\"image4.png") RequestBody imgs4);

如果图片数量不确定

  @Multipart
 @POST()
  Observable<ResponseBody> uploadFiles(
        @Url String url,
        @Part("filename") String description,
        @PartMap() Map<String, RequestBody> maps);

或者:

@Multipart
@POST()
Observable<ResponseBody> uploads(
        @Url String url,
        @Part("description") RequestBody description,
        @Part("filekey") MultipartBody.Part file);

图文同时上报

Part方式

@Multipart
@POST("upload/")
Call<ResponseBody> register(
                                   @FieldMap Map<String , String> usermaps,
                                   @Part("avatar\\\\\\\\\\\\\\\\"; filename=\\\\\\\\\\\\\\\\"avatar.jpg") RequestBody avatar,
                                   );

扩展一下:

@Multipart
@POST
Observable<ResponseBody> uploadFileWithPartMap(
        @Url() String url,
        @PartMap() Map<String, RequestBody> partMap,
        @Part("file") MultipartBody.Part file);

java代码:

RequestBody requestFile =
            RequestBody.create(MediaType.parse("multipart/form-data"), file);

    // MultipartBody.Part is used to send also the actual file name
    MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), requestFile);

    // create a map of data to pass along
    RequestBody tokenBody = RequestBody.create(
            MediaType.parse("multipart/form-data"), token);

    HashMap<String, RequestBody> map = new HashMap<>();
    map.put("token", tokenBody);

    Call<ResponseBody> call = service.uploadFileWithPartMap(url, requestBody );
   // 执行
  call.enqueue(new Callback<ResponseBody>() {
   @Override
   public void onResponse(Call<ResponseBody> call,
    Response<ResponseBody> response) {
       Log.v("Upload", "success");
    }

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

更简单的用Body方式:

 @POST()
Call<ResponseBody> upLoad(
   @Url() String url,
   @Body RequestBody Body);

Java代码:

   //构建body
   RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
      .addFormDataPart("name", name)
      .addFormDataPart("name", psd)
      .addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("image/*"), file))
      .build();

   Call<ResponseBody> call = service.upload(url, requestBody );
  // 执行
 call.enqueue(new Callback<ResponseBody>() {
   @Override
   public void onResponse(Call<ResponseBody> call,
    Response<ResponseBody> response) {
       Log.v("Upload", "success");
    }

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

此种方式让你很好的解决了用户注册问题,包含用户全部信息和头像,完美解决你想用表单一起将文字和图片一起提交的情况!

表单提交

很多时候想用表单的方式:

@Multipart
@POST("upload/")
Call<ResponseBody> register(
       @Body RequestBody body
                                   );

Java代码:

   RequestBody requestFile =
RequestBody.create(MediaType.parse("multipart/form-data"), file);

  Call<ResponseBody> call = service.register(body);  
Response<ResponseBody> response = call.execute();

Json提交

上传Json

@POST("/uploadJson")
Observable<ResponseBody> uploadjson(
        @Body RequestBody jsonBody);

upLoadJson 也可以具体指明Content-Type 为 “application/json”格式的

具体组装我们的RequestBody则可以这样:

 RequestBody body= 
               RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"), jsonString);

接着可以这样调用:

// 执行请求
Call<ResponseBody> call = service.uploadJson(description, body);
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call,
                           Response<ResponseBody> response) {
        Log.v("Upload", "success");
    }

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

}

至于服务器返回什么类型的model, 开发者可以自定义, 譬如你可以把APi 中的 ResponseBody 指定为你自己的javaBean, 当然上层构建Call时候,Callback也必须是 Call<MyBean>

@POST("/uploadJson")
Call<MyBean> uploadjson(
        @Body RequestBody jsonBody);

仔细的朋友会发现有的地方用Call,有的地方用Observable,如果结合了RxJava就是用后者接收了,这里不关乎什么方式。

上面的代码片段中显示的代码初始化(RequestBody 和description), 以及如何使用文件上传API。正如刚开始已经提到的, 从OkHttp 的RequestBody类中,需要两个RequestBody.create()方法

除了Body描述, 必须将添加文件包装成MultipartBody的实例。这就是你需要使用适当的从客户端上传文件到服务端。此外, 您可以添加createFormData中的uploadFile(Uri fileUri)方法,适合相机拍照回来上传图片的场景。

  public static File getFile(Context context, Uri uri) {
    if (uri != null) {
        String path = getPath(context, uri);
        if (path != null && isLocal(path)) {
            return new File(path);
        }
    }
    return null;
}

通过以上代码片段,可以构造自己的file, 其他构建ResquestBody的步骤同上所示,以上案列总有一款适合你的web后端。

设置 Content-Type

请注意设置的内容类型。如果你拦截底层OkHttp客户端和更改内容类型为application / json, 你的服务器可能反序列化过程出现的问题。请确保你没有自定义更改multipart/form-data。

upLoad图片也可以具体指明Content-Type 为 “image/jpg”格式的

   RequestBody requestFile =
            RequestBody.create(MediaType.parse("image/jpg"), mFile);

上传文件到服务端示例

如果你已经有你的后端项目, 您可以依靠下面的示例代码。我们使用一个简单api 上传到服务器。此外我们告诉api 传入参数的请求, 因为我们使用的是Node.js

解析的回调函数,我们记录每个字段来显示其输出。

method: 'POST', 
 path: '/upload',  
config: {  
payload: {
    maxBytes: 209715200,
    output: 'stream',
    parse: false
},
handler: function(request, reply) {
    var multiparty = require('multiparty');
    var form = new multiparty.Form();
    form.parse(request.payload, function(err, fields, files) {
        console.log(err);
        console.log(fields);
        console.log(files);

        return reply(util.inspect({fields: fields, files: files}));
    });
}}

Android客户端收到返回类型的字符串, 我们将接收到的上传成功的状态的回调。当然你可以处理也可以不处理状态。下面你将看到一个成功的请求的输出端和有效载荷的解析。第一个空对象。之后,你可以看到字段只描述作为请求的一部分。接着可以收到文件描述,文件大小,文件昵称和保存路径。

服务器解析有效数据的日志

   Null
  { description: [ 'hello, this is description speaking' ] }
{ picture:
  [ { fieldName: 'picture',
    originalFilename: '20160312_095248.jpg',
    path:      '/var/folders/rq/q_m4_21j3lqf1lw48fqttx_80000gn/T/X_sxX6LDUMBcuUcUGDMBKc2T.jpg',
   headers: [Object],
   size: 39369 } ] }

回顾

文件上传是应用程序中必不可却少的功能, 你可以将此功能集成在您的应用程序使用。本文指导您完成你的Android程序上报文件到您的后端服务器的第一个步骤。

源码:https://github.com/Tamicer/Novate

Retrofit 2.0系列请阅读简书
更多技术文章请关注码小白

Retrofit 2.0+RxJava系列请阅读

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值