zoukankan      html  css  js  c++  java
  • 【Android实战】----基于Retrofit实现多图片/文件、图文上传

    本文代码详见: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>() {
                @Override
                public 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);
                    }
                }
    
                @Override
                public 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?M K5x#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??

    ?{??

    ?

    ?7 S?

    ??

    ??

    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?

    ?

    ?

    ?s n??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();
    			}
    		}






    
    
    
  • 相关阅读:
    win10远程桌面连接提示身份验证错误,要求的函数不受支持的解决方案
    十六进制转八进制
    十六进制转十进制
    十进制转十六进制
    LEETCODE
    LINUX
    LINUX
    LEETCODE
    LEETCODE
    LEETCODE
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/7298464.html
Copyright © 2011-2022 走看看