本文代码详见:https://github.com/honghailiang/RetrofitUpLoadImage

一、再次膜拜下Retrofit

Retrofit无论从性能还是使用方便性上都很屌!!!,本文不去介绍其运作原理(虽然很想搞明白),后面会出专题文章解析Retrofit的内部原理;本文只是从使用上解析Retrofit实现多图片/文件、图文上传的功能。文件上传相关可参考Multipart/form-data文件上传简介 及Apache FileUpload文件上传功能 。

二、概念介绍

1)注解@Multipart

从字面上理解就是与多媒体文件相关的,没错,图片、文件等的上传都要用到该注解,其中每个部分需要使用@Part来注解。。看其注释

/*** Denotes that the request body is multi-part. Parts should be declared as parameters and* annotated with {@link Part @Part}.*/

2)注解@PartMap

当然可以理解为使用@PartMap注释,传递多个Part,以实现多文件上传。注释

/*** Denotes name and value parts of a multi-part request.* <p>* Values of the map on which this annotation exists will be processed in one of two ways:* <ul>* <li>If the type is {@link okhttp3.RequestBody RequestBody} the value will be used* directly with its content type.</li>* <li>Other object types will be converted to an appropriate representation by using* {@linkplain Converter a converter}.</li>* </ul>* <p>* <pre><code>* @Multipart* @POST("/upload")* Call<ResponseBody> upload(*     @Part("file") RequestBody file,*     @PartMap Map<String, RequestBody> params);* </code></pre>* <p>* A {@code null} value for the map, as a key, or as a value is not allowed.** @see Multipart* @see Part*/

3)RequestBody

从上面注释中就可以看到参数类型是RequestBody,其就是请求体。文件上传就需要参数为RequestBody。官方使用说明如下http://square.github.io/retrofit/

Multipart parts use one of Retrofit's converters or they can implement RequestBody to handle their own serialization.

四、基本实现

了解了以上概念,下面就一一实现

1)接口定义

public interface IHttpService {
@Multipart@POST("file/upLoad.do")Call<BaseBean> upLoadAgree(@PartMap Map<String, RequestBody>params);
}

BaseBean是根据服务端返回数据进行定义的,这个使用时可以根据自有Server定义。

2)Retrofit实现

/*** Created by DELL on 2017/3/16.* 上传文件用(包含图片)*/public class RetrofitHttpUpLoad {/*** 超时时间60s*/private static final long DEFAULT_TIMEOUT = 60;private volatile static RetrofitHttpUpLoad mInstance;public Retrofit mRetrofit;public IHttpService mHttpService;private static Map<String, RequestBody> params;private RetrofitHttpUpLoad() {mRetrofit = new Retrofit.Builder().baseUrl(UrlConfig.ROOT_URL).client(genericClient()).addConverterFactory(GsonConverterFactory.create()).build();mHttpService = mRetrofit.create(IHttpService.class);}public static RetrofitHttpUpLoad getInstance() {if (mInstance == null) {synchronized (RetrofitHttpUpLoad.class) {if (mInstance == null)mInstance = new RetrofitHttpUpLoad();params = new HashMap<String, RequestBody>();}}return mInstance;}/*** 添加统一超时时间,http日志打印** @return*/private OkHttpClient genericClient() {HttpLoggingInterceptor logging = new HttpLoggingInterceptor();logging.setLevel(HttpLoggingInterceptor.Level.BODY);OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(logging).connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS).writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS).readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS).build();return httpClient;}/*** 将call加入队列并实现回调** @param call             调入的call* @param retrofitCallBack 回调* @param method           调用方法标志,回调用* @param <T>              泛型参数*/public <T> void addToEnqueue(Call<T> call, final RetrofitCallBack retrofitCallBack, final int method) {final Context context = MyApplication.getContext();call.enqueue(new Callback<T>() {@Overridepublic void onResponse(Call<T> call, Response<T> response) {LogUtil.d("retrofit back code ====" + response.code());if (null != response.body()) {if (response.code() == 200) {LogUtil.d("retrofit back body ====" + new Gson().toJson(response.body()));retrofitCallBack.onResponse(response, method);} else {LogUtil.d("toEnqueue, onResponse Fail:" + response.code());ToastUtil.makeShortText(context, "网络连接错误" + response.code());retrofitCallBack.onFailure(response, method);}} else {LogUtil.d("toEnqueue, onResponse Fail m:" + response.message());ToastUtil.makeShortText(context, "网络连接错误" + response.message());retrofitCallBack.onFailure(response, method);}}@Overridepublic void onFailure(Call<T> call, Throwable t) {LogUtil.d("toEnqueue, onResponse Fail unKnown:" + t.getMessage());t.printStackTrace();ToastUtil.makeShortText(context, "网络连接错误" + t.getMessage());retrofitCallBack.onFailure(null, method);}});}/*** 添加参数* 根据传进来的Object对象来判断是String还是File类型的参数*/public RetrofitHttpUpLoad addParameter(String key, Object o) {if (o instanceof String) {RequestBody body = RequestBody.create(MediaType.parse("text/plain;charset=UTF-8"), (String) o);params.put(key, body);} else if (o instanceof File) {RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data;charset=UTF-8"), (File) o);params.put(key + "\"; filename=\"" + ((File) o).getName() + "", body);}return this;}/*** 构建RequestBody*/public Map<String, RequestBody> bulider() {return params;}public void clear(){params.clear();}
}

其中定义了Retrofit实例、还用拦截器定义了统一的超时时间和日志打印;将call加入队列并实现回调。最重要的就是添加参数:

 /*** 添加参数* 根据传进来的Object对象来判断是String还是File类型的参数*/public RetrofitHttpUpLoad addParameter(String key, Object o) {if (o instanceof String) {RequestBody body = RequestBody.create(MediaType.parse("text/plain;charset=UTF-8"), (String) o);params.put(key, body);} else if (o instanceof File) {RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data;charset=UTF-8"), (File) o);params.put(key + "\"; filename=\"" + ((File) o).getName() + "", body);}return this;}

这里就是根据传入的参数,返回不同的RequestBody, 注意文件的key值。

3)使用

private void upLoadAgree() {showWaitDialog();RetrofitHttpUpLoad retrofitHttpUpLoad = RetrofitHttpUpLoad.getInstance();retrofitHttpUpLoad.clear();if (!StringUtil.isEmpty(pathImage[0])){retrofitHttpUpLoad = retrofitHttpUpLoad.addParameter("pic1",new File(pathImage[0]));}if (!StringUtil.isEmpty(pathImage[1])){retrofitHttpUpLoad = retrofitHttpUpLoad.addParameter("pic2", new File(pathImage[1]));}if (!StringUtil.isEmpty(pathImage[2])){retrofitHttpUpLoad = retrofitHttpUpLoad.addParameter("zip", new File(pathImage[2]));}Map<String, RequestBody> params = retrofitHttpUpLoad.addParameter("status", "4").addParameter("pickupId", tv_orderquality_pid.getText().toString()).addParameter("cause", reason).addParameter("connectname", et_orderquality_lxrname.getText().toString()).addParameter("connectphone", et_orderquality_lxrphone.getText().toString()).addParameter("details", et_orderquality_xqms.getText().toString()).bulider();retrofitHttpUpLoad.addToEnqueue(retrofitHttpUpLoad.mHttpService.upLoadAgree(params),this, HttpStaticApi.HTTP_UPLOADAGREE);}

需要注意的是要对图片及文件路径进行判空操作,否则会报异常W/System.err: java.io.FileNotFoundException: /: open failed: EISDIR (Is a directory)

五、报文日志

其中图片报文有省略

D/OkHttp: --> POST http://192.168.xxx.xxx:8880/xxx/nocheck/file/agree.do http/1.1
D/OkHttp: Content-Type: multipart/form-data; boundary=f3e7369a-ead9-46e2-9ddd-448442fd5108
D/OkHttp: Content-Length: 300580
D/OkHttp: --f3e7369a-ead9-46e2-9ddd-448442fd5108
D/OkHttp: Content-Disposition: form-data; name="pic2"; filename="90079.jpg"
D/OkHttp: Content-Transfer-Encoding: binary
D/OkHttp: Content-Type: multipart/form-data;charset=UTF-8
D/OkHttp: Content-Length: 149456
D/OkHttp: ??????JFIF????H??H????????C??
D/OkHttp:   "##!  %*5-%'2(  .?/279<<<$-BFA:F5;<9????C
D/OkHttp: 9& &99999999999999999999999999999999999999999999999999?????8"????????????????????????????????????????????????????????????????????|???/n?s??y?]8ug?7?R??????h?tΘa?T?T<?U???z???+3C?w??tdf??=<??????fN??s??x??hhzd??X~?X??i?{~/?<^??~=zX??\??4?U?ɡ)I???????????????????$??@??? iM?"J?R 2?f?MK5x#w?????r?I?3?Y??l???V?Bj??>{??t?u????]????g>?o??o???dM?U??????J?R??<?+?;??????????OG>????=?5?L?9?&???_/\?yu??~|*,???My?r????????='?d?=?t*??*?Y??(????????????????  YB i?Jlv??d?"l????Y??4??X?7???;??sY?\κ+?N??;?L??&?(?MJ???@w~~???a?qs??m7??y???Ns?\?C?g??>???N%??N??gs?Q??c????Z?t???x??{??^_}s?s??gO?????N?|}?;??y?y?ǎ|?v??N?l???????????*k5?(?????????1$?B??j?+,?l???hN??U?<sgb?g?x?S??;;c?,??7?0Z?J?I?r??X?9?t?'\?1W+
D/OkHttp: [?? ????=/X???n??T*4?u??<?????s?q??????c???\?6?YV?p????oB%??|?s??????????{??g??k?}?t??d{]^W???v?WB?x???|z9?>V?{ǒ>?o??Y????xk?k7???{w????b?!JQjRQ%RH?%8?H??Q??Ys?{???u??(?`?b\??k?cC:u#???d?C??&??W.CXd?e??N??n????.?%v?,.zW?>??&??+??r??S'?.n?[   V?  ?q??oGL?,:?S??????/?o<???,?B???;??????^[?#Lm??7.Q?6sONz??fwN?
D/OkHttp: ???,?\????
D/OkHttp: ???U<???1?Z???=??pn?~q??[-?P??=?j?va?R?  ??4X*???nv?H??j?j?p??`h#-???qiA?U?????x?&v?b?R??o?.??H[M5??Y??5?>%Y?j????x?2.m??=??GG???
D/OkHttp: \?D??(?JK9<J???JE?jl??pW
D/OkHttp: ??}??i?6??R??:!J??FT?!e???
D/OkHttp: ????:??5????%??`?|???;z?G??[?P???N=???T??X?-?okNe???Y??f8?`:?P???x?1?I?g   ?0?)?fe*P?qU?~?jSY%??gɡR?(?$A?|y?}??s?2?<?/??4?s??@-?,??AZ?az?,??bl?.??WD??????q?X?u}?+G?z?h8=?w[`?j??g&q?c????????<|??|? 1????q^???
D/OkHttp: 5??)x???:*dK??|?KPz5v?4?+?>eO?4??i?P2F?&\9???? -V?esf~&F?Q??S?\???8{??*B?1qVO????-S??!?????????*6??
D/OkHttp: 3?5W?r?x??+?\?r???6??C??Ms,H?AE??=q??????(??f?=,????Z??+????L??<??v_i-?m|????6??L???=?4?Y?{?W??9=??\AW???~??{@!?^ Z6??,?k>|??C
D/OkHttp: aZ??-?ы??R?K???1?6`'??F%/4wZ?Cn????[?]?el??U&[???1db-4????????~er!?4??>ji?]??AV?[v??e??`θo???帏!(??Pk?XCI??Glk-p??p  ?B?b???ae???d?]-|"??*??`??l??Tmn`???
D/OkHttp: R?G??h?DETp???i???^????u?E??1?wW%?<????????3e??V????   **m??9V??O?R??f?b?
D/OkHttp: ??j%)^?$??g?\?Qm^`??
D/OkHttp: ? ?[*?\?@/T@,?|@\$F?????v_??uA???:?9>%B????1 =?D]{?"??*^??q1??i??B?bs?L_???? e??W?2??pDR?Q??M?? ?{????7S?????Dzcb\??????0?????u? h???e??7X??)?s{??DIqf???QdM??V?????r??MTk?=??+?>b0?b???i?\?lI??H?P??,?Pn[]??.?`.X?=A?I?P?@?<~??Px??.???9?(??:=??5E?n?!l??? 5???ee_??'[????p?d??1?)g?s<????kop?вd?19m?ft??ab????????j?5/pT?M?xBb??8???z??????wX??V??|~x????????c?Rsx???D???ixH??ud?50??MΘ7???<?^I???i?`?????f?A?????????;?U?H????a~?W臸?@O?'u\-???????CN,????-???@?+"n?:y???G |S??C?F5?????Ix?????)?????b 22???jRj????j?,K?K"¥</?G?w/  *?W????sn??L????n?n????? k????"?*??~?9??<4?,c?d>?EG??iB??0+??i?Y??D???p???????S|.?2???# &??"v?Y??P??O?#EK???,J?6U?>a???;?-rM??@???^b??@??K?????PI??4?qM|??V??h[Ld??R????or?U?M??)_?J?^S?41n}?@n|??
D/OkHttp: --f3e7369a-ead9-46e2-9ddd-448442fd5108
D/OkHttp: Content-Disposition: form-data; name="cause"
D/OkHttp: Content-Transfer-Encoding: binary
D/OkHttp: Content-Type: text/plain;charset=UTF-8
D/OkHttp: Content-Length: 33
D/OkHttp: 对货物数量、质量有异议
D/OkHttp: --f3e7369a-ead9-46e2-9ddd-448442fd5108
D/OkHttp: Content-Disposition: form-data; name="details"
D/OkHttp: Content-Transfer-Encoding: binary
D/OkHttp: Content-Type: text/plain;charset=UTF-8
D/OkHttp: Content-Length: 9
D/OkHttp: 哈哈哈
D/OkHttp: --f3e7369a-ead9-46e2-9ddd-448442fd5108
D/OkHttp: Content-Disposition: form-data; name="status"
D/OkHttp: Content-Transfer-Encoding: binary
D/OkHttp: Content-Type: text/plain;charset=UTF-8
D/OkHttp: Content-Length: 1
D/OkHttp: 4
D/OkHttp: --f3e7369a-ead9-46e2-9ddd-448442fd5108
D/OkHttp: Content-Disposition: form-data; name="pickupId"
D/OkHttp: Content-Transfer-Encoding: binary
D/OkHttp: Content-Type: text/plain;charset=UTF-8
D/OkHttp: Content-Length: 6
D/OkHttp: 105329
D/OkHttp: --f3e7369a-ead9-46e2-9ddd-448442fd5108
D/OkHttp: Content-Disposition: form-data; name="connectphone"
D/OkHttp: Content-Transfer-Encoding: binary
D/OkHttp: Content-Type: text/plain;charset=UTF-8
D/OkHttp: Content-Length: 11
D/OkHttp: 13xxxxxxxxx
D/OkHttp: --f3e7369a-ead9-46e2-9ddd-448442fd5108
D/OkHttp: Content-Disposition: form-data; name="connectname"
D/OkHttp: Content-Transfer-Encoding: binary
D/OkHttp: Content-Type: text/plain;charset=UTF-8
D/OkHttp: Content-Length: 3
D/OkHttp: 111
D/OkHttp: --f3e7369a-ead9-46e2-9ddd-448442fd5108--
D/OkHttp: --> END POST (300580-byte body)

六、代码托管

https://github.com/honghailiang/RetrofitUpLoadImage

七、效果图:

八、服务端代码

FileItemFactory factory = new DiskFileItemFactory();ServletFileUpload upload = new ServletFileUpload(factory);File directory = null;List<FileItem> items = new ArrayList<FileItem>();InputStream is = null;FileOutputStream fos = null;try {items = upload.parseRequest(request);// 得到所有的文件Iterator<FileItem> it = items.iterator();while (it.hasNext()) {FileItem fItem = (FileItem) it.next();String fName = "";Object fValue = null;if (fItem.isFormField()) { // 普通文本框的值fName = fItem.getFieldName();fValue = fItem.getString("UTF-8");if("pickupId".equals(fName)){pickupAppeal.setPickupId(fValue.toString());}else if("cause".equals(fName)){pickupAppeal.setCause(fValue.toString());}else if("connectname".equals(fName)){pickupAppeal.setConnectname(fValue.toString());}else if("connectphone".equals(fName)){pickupAppeal.setConnectphone(fValue.toString());}else if("details".equals(fName)){pickupAppeal.setDetails(fValue.toString());}else if("status".equals(fName)){String status = fValue.toString();BigDecimal big = new BigDecimal(status);pickupAppeal.setStatus(big);}//map.put(fName, fValue);} else { // 获取上传文件的值fName = fItem.getFieldName();fValue = fItem.getInputStream();String name = fItem.getName();if (name != null && !("".equals(name))) {name = name.substring(name.lastIndexOf(File.separator) + 1);int lenN =name.indexOf(".");String suf = name.substring(lenN, name.length());String day = DateUtil.format(Calendar.getInstance().getTime(), "yyyy-MM-dd");String path = PATH+File.separator+day;directory = new File(path);if (!directory.exists()) {directory.mkdirs();}String preFile =UUID.randomUUID().toString().replace("-", "");String filePath = path + File.separator+preFile+suf;String serverPath = day+ File.separator+preFile+suf;map.put(fName,  serverPath);map.put(fName+"m", name);is = fItem.getInputStream();fos = new FileOutputStream(filePath);byte[] buffer = new byte[1024];int len = 0;while ((len = is.read(buffer, 0, 1024)) != -1) {fos.write(buffer, 0, len);}fos.flush();}}}} catch (Exception e) {pLog.error("接收参数错误:" + e.getMessage());resultInfo.setStatus(ComStatus.RESULTINFO_STATUS_FAILE);resultInfo.setMessage(ComStatus.RESULTINFO_MESSAGE_FAILE);resultInfo.setData(map);resReslt.setResData(resultInfo);} finally {try {if (null != fos) {fos.close();}if (null != is) {is.close();}} catch (IOException e) {e.printStackTrace();}}




【Android实战】----基于Retrofit实现多图片/文件、图文上传相关推荐

  1. 使用jQuery开发一个基于HTML5的漂亮图片拖拽上传web应用

    昨天我们介绍了一款HTML5文件上传的jQuery插件:jQuery HTML5 uploader,今天我们将开发一个简单的叫upload center的图片上传程序,允许用户使用拖拽方式来上传电脑上 ...

  2. 大文件分片上传前端框架_基于Node.js的大文件分片上传

    基于Node.js的大文件分片上传 我们在做文件上传的时候,如果文件过大,可能会导致请求超时的情况.所以,在遇到需要对大文件进行上传的时候,就需要对文件进行分片上传的操作.同时如果文件过大,在网络不佳 ...

  3. java实现七牛云图片文件的上传

    java实现七牛云图片文件的上传 七牛云:https://portal.qiniu.com/create#resource 首先需要去注册一个账号实现实名认证 之后打开七牛云的 我们需要先创建一个储存 ...

  4. Java实现图片文件的上传和获取

    在做一个网站开发时,难免会遇到图片等文件的上传和获取,举一个简单的例子,就是用户的头像问题,我们应该怎么上传,怎么获取,上传到哪呢,等一系列的问题,其实像图片这些文件,存储在云存储空间比较合适,但是收 ...

  5. 使用富文本编辑器wangEditor完成图片文件的上传

    项目中一些特定的功能可能需要在页面中用到富文本编辑器来实现文件的输入上传等等,在这里用wangEditor来实现使用富文本编辑器进行文件图面的输入和上传保存,这里wangEditor也可以参考wang ...

  6. html5 上传图片模板,HTML5实现图片文件异步上传

    ,过现前个能文使近记接的端问对字用近记接 利用HTML5的新特点做文件异步上传非常简单方便,本文主要展示JS部分,html结构.下面的代码并未使用第三发库,如果有参照,请注意一些未展现出来的代码片段. ...

  7. android 本地html传递参数,Android WebView适配html加载本地文件并上传

    写在前面 自从做Android APP产品以来,版本更迭一版接着一版,为新的业务需求一步也未曾停歇.工作开始进入一种机械化的快节奏:接触新知识,新技术领域一般都是通过网上找资料,快速入门,遇到问题解决 ...

  8. (转)使用MFC的WinINet库来实现图片文件的上传

    VC++用来进行Internet客户端编程的基础知识的介绍,主要见这个网址: http://msplinks.com.cn/MDFodHRwOi8vd3d3LnZja2Jhc2UuY29tL2RvY3 ...

  9. dropzone.js php,Laravel 5.1 中基于 Dropzone.js 实现图片拖拽上传及删除功能

    注:本教程代码适用于Laravel 5.1版本. 1.概述 Dropzone 是目前最好的免费文件拖拽上传库,它拥有很多特性和选项以便你可以使用多种方式来自定义. 在Laravel项目集成Dropzo ...

最新文章

  1. Tomcat 架构原理解析到架构设计借鉴
  2. 选哪扇门得奖金的算法
  3. 探索 .NET团队对API的设计流程
  4. 13、Math类简介
  5. live2d手机制作软件_live2d制作器手机版
  6. linux sqluldr2命令,sqluldr2 linux64
  7. 地理信息安全在线培训考试系统题库-多选题
  8. 采样定理的证明与推导
  9. 查询计算机的配置命令是什么,查看电脑配置指令大全
  10. 使用HTML批量拼图
  11. linux输入文件后clustalw,ClustalW----多序列比对分析(一)
  12. 完美世界(完美世界(北京)网络技术有限公司)
  13. 关于mac os更新到monterey, MATLAB出现Warning: the font “Times“ is not available, so “Lucida Bright“ has
  14. win7系统无法更新计算机配置,win7提示无法更新启动配置怎么办
  15. 网易考拉根据ID取商品详情 API 返回值说明
  16. 相遇3000亿美金之巅,阿里腾讯战力与血值几何?
  17. Beginning WF4读书笔计 - 第一章 03设计示图及xaml代码
  18. 线条边框简笔画图片大全_黑白简笔画与ps技巧
  19. scrapy爬取网易云音乐的MP3链接
  20. 深度学习基础——多层感知机

热门文章

  1. php生成统计图类pchart---中文翻译手册
  2. golang string转int8_Go教程:23 string转换int类型方法对比
  3. BZOJ 1507 Editor
  4. CAD修复块中心(com接口c#语言)
  5. java编写一个可切换的界面_java web 项目实现手动中英文切换
  6. 局域网服务器如何设置代理访问公网
  7. 区块链触手可及 复杂美BAAS平台
  8. 输入数独题目,程序输出数独的唯一解。保证所有已知数据的格式都是合法的,并且题目有唯一的解。
  9. 台式电脑属于小型电子计算机,个人计算机属于小型计算机。
  10. 空间几何变换 之 齐次坐标