用retrofit上传图片与下载以及进度

来源:互联网 发布:最权威的行业数据 编辑:程序博客网 时间:2024/06/14 00:30

1、retrofit真的是一个很强的框架,尤其是跟Rxjava结合使用,这样真的无敌。不过对于不同的后台要求,前端传参是不一样的。首页本篇博客后面会讲一些我自己对于不同的后台框架以及如何传递数据和解析数据的经验。

2、前端传文件(图片和txt等)到后台是要看后台要的是什么,如果后台要的是File,后台代码如下:

package com.daka.action;import java.io.ByteArrayInputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.util.HashMap;import java.util.List;import java.util.Map;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import org.apache.commons.fileupload.RequestContext;import org.apache.commons.io.FileUtils;import org.apache.struts2.ServletActionContext;import org.omg.CORBA.Request;import com.opensymphony.xwork2.ActionSupport;/** * 图片上传 * @author Administrator * */public class UploadImageAction extends ActionSupport{private File upload;private String uploadContentType;private String uploadFileName;private Map<String,Object> result;public File getUpload() {return upload;}public void setUpload(File upload) {this.upload = upload;}public String getUploadContentType() {return uploadContentType;}public void setUploadContentType(String uploadContentType) {this.uploadContentType = uploadContentType;}public String getUploadFileName() {return uploadFileName;}public void setUploadFileName(String uploadFileName) {this.uploadFileName = uploadFileName;}public Map getResult() {return result;}public String doUpload() {String filePath = ServletActionContext.getServletContext().getRealPath("/upload");System.out.println(filePath);File file = new File(filePath);if(!file.exists()){file.mkdir();}if(upload == null){result = new HashMap<String,Object>();result.put("result", "上传失败!") ;result.put("info", "上传文件为空");return "success";}//String[] filenames = new String[upload.size()];try {FileUtils.copyFile(upload, new File(file,uploadFileName));} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}result = new HashMap<String,Object>();result.put("result", "上传成功!") ;result.put("filenames", uploadFileName);return "success";}}

那么前端用Retrofit如下调用该接口:

retrofit的初始化代码我就不贴出来了,那个网上到处有

(1)写调接口的方法:

//上传图片    @Multipart    @POST("upload2!doUpload.action")    Observable<UploadResponse> uploadPic(@Part("upload\"; filename=\"cy.png") RequestBody body, @Query("uploadFileName") String name);

我解释一下上面的参数:

POST括号里面的是接口的方法名;

@Part括号里面的是重点,“upload”就是对于的你的后台拿你上传图片的那个键,我贴出来的后台代码的键就是upload,当然很多人贴出来的可能是“file”,这是要看你后台给你的键是什么;

"cy.png"是你自己写的, 这个是后台拿着你的文件写在服务器硬盘里存储的时候保存的名字;

@Part("upload\;filename=\cy.png")这个格式是固定格式,你只能修改我上面说的两个地方。


(2)ViewModel:

 public Observable<UploadResponse> upload() {        return DAL.shareDAL.baseAPIService().uploadPic(requestBody, "IMG_20170529_213655.jpg");    }
额,我上面这个代码忘记写入参了,RequestBody requestbody


(3)在activity中调用viewmodel里面的上传方法:

  private final TestViewModel viewModel = new TestViewModel();        Bitmap bitmap = PicUtils.lessenUriImage("/storage/emulated/0/Pictures/监理/IMG_20170529_213655.jpg");        ByteArrayOutputStream os = new ByteArrayOutputStream();        bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);        RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), os.toByteArray());        viewModel.upload().subscribe(uploadResponse -> {        }, e -> {            ToastUtils.showToast(e.getMessage());        });
上面这几行代码我忘记把我写的requestBody防盗upload()的括号里面了,不好意思。


3.如果后台让你传流给他,这个后台代码我没有,我把我写的客户端代码贴出来并解释一下:

我们公司这个图片是放在阿里的oss平台上的,我们的后台要做的事是让我传一个流和一些其他信息给他,其他信息我就不贴出来了,我会解释那些事其他无用信息:

(1)接口方法:

  @Multipart    @POST("ossUploadFile")    Observable<UploadFileResponse> uploadPic(@Part("filename") UploadFileIn options, @Part("root") RequestBody requestBody);

这个传参跟上传那个传File是不一样的,@part后面跟的就是键,跟上面的那个键是一样的意思,但是却写法不一样,最后造成的效果就不一样,这个写服务器拿到的是一个String类型的对象,他需要自己转换成流再转换成图片,当然有些框架是直接可以拿到流,如果可以直接拿到就更好,如果不能拿到就要自己转换,不转换就会造成后台崩溃,后台在走setFile的时候就会报类型转换错误而崩掉。所以如果后台要是麻烦或者是不会转换乃至转换失败的话我建议用我上面介绍的传File的形式来传递文件,这样比较简单。


(2)viewModel:是我写的一个普通的类

public class UploadFileViewModel {    public Observable<UploadFileResponse> uploadFile(UploadFileIn options, RequestBody params) {        return DAL.shareDAL.baseAPIService().uploadPic(options, params);    }    public Observable<ResponseBody> downFile(String in) {        return DAL.shareDAL.downloadPicService().downloadPic(in);    }    public Observable<BaseResponse> delectPic(DelectPicIn entity) {        return DAL.shareDAL.baseAPIService().delectPic(entity);    }}

上面的upload是调用上传接口的上传方法。另外两个是删除附件以及下载附件的方法,我没有贴出相应代码。我稍微介绍一下,下载拿到的是一个ResponseBody,然后这个ResponseBody本身就带获取流的方法,拿到流就好办了。


(3)在activity里面调用方法:

 PicUtils.lessenUriImage(path).compress(Bitmap.CompressFormat.PNG, 100, os);                uploadFileIn.setSize(os.toByteArray().length + "byte");                RequestBody photo = RequestBody.create(MediaType.parse("application/octet-stream"), os.toByteArray());                uploadViewModel.uploadFile(uploadFileIn, photo)                        .subscribe(uploadFileResponse -> {                            if (uploadFileResponse.code == 50000) {                                                             //下载完毕了刷新gridview                                    commonDialog.dismiss();                                    refreshPicData();                                                      } else {                                commonDialog.dismiss();//上传失败                            }                        }, e -> {                            commonDialog.dismiss();//上传出错                            ToastUtils.showToast(activity, e.getMessage());                        });

这里是调用viewModel的方法。你只需要在意uploadFile括号里面第二个参数就行,第一个参数是我传递的其他信息。

4、用retrofit跟struts2交互的时候有可能后台说要你传递对象给他来使用,当然有些事传一个对象转换的json字符串给他。这个其实都一样,如果前端传的是对象转换的json给后台,后台就需要自己来吧string转换成对象来使用。如果要求前端传递对象就需要我们用表单的形式吧对象的每一个字段进行赋值,这样后台拿到的就是直接一个对象,这样后台就方便。具体操作就看你们商量了。

5、其实如果有些后台是可以直接跟andorid传递对象(从广义上来说)。比如我下面的代码:

 //登录获取个人信息    @POST("connectTerracePortService")    Observable<LoginResponse> login(@Body LoginIn entity);

我传递给后台就是LoginIn对象。后台接到的也是对象,两端都不需要做什么字符串跟对象的转换。这其实对于retrofit
来说是本身带的功能,但是有些后台是没有这样的功能的,比如struts就没有。其实这样是通过注解实现的,我上一个项目就是用的这种方式来完成整个项目的。

6、我们在解析服务器传过来的数据的时候,默认的规则是{}里面装的是对象,[]里面装的是集合list,但是有些后台觉得字段比较少(其实是比较懒)然后就把一个集合不装载list里面,而是放在一个map里面直接反回了.数据可能如下:(1)

{code:500,

  result:{

    cy=27,

    li=30,

    hu=18

   }

}

其实我们想拿到的数据样子是(2):

{code:500,

  result: [ 

     {name:cy,age:27},

     {name:li,age:30},

     {name:hu,age:18}

   ]

}

如果是(2)这种方式解析起来就好办多了,不过是(1)的方式也不难办,

解析方式如下:

public class GetNodeResponse extends WorkFlowBaseResponse {    @SerializedName("result")    public JsonObject entity;}
先暂时用JsonObject拿到那个map对象

  public static Map<String, Object> reloveJsonToToMap(String jsonStr)            throws JsonSyntaxException    {        Map<String, Object> objMap = null;        if (gson != null)        {            Type type = new TypeToken<Map<String, ?>>()            {            }.getType();            objMap = gson.fromJson(jsonStr, type);        }        return objMap;    }
Map<String, Object> stringObjectMap = JsonUtils.reloveJsonToToMap(getNodeResponse.entity.toString());                Set<String> keySet = stringObjectMap.keySet();                Iterator<String> iterator = keySet.iterator();                List<User> users= new ArrayList<User>();                while (iterator.hasNext()) {                    User user = new User();                    String key = iterator.next();                    user .setName(key);                    user .setAge((String) stringObjectMap.get(key));                    users.add(user);                }
我上面写的JsonUtils.reloveToMap括号里面就是上面通过键值对去到的数据,然后进行转换成List<User>。

以上就是我暂时对retrofit传文件、对象、已经解析map等的基本看法。

然后retrofit的自定义转换器我现在暂时没有自己深刻的体会来进行组织语言描述,等再熟悉一些再介绍一下自定义的转换器等的内容。





原创粉丝点击