使用OkHttp上传图片

来源:互联网 发布:淘宝给老客户发优惠券 编辑:程序博客网 时间:2024/06/14 19:04

简介

上传图片是一个APP的常见功能,可以是通过OOS上传到阿里云,也可以直接上传到Server后台,OOS有提供相应的SDK,此处忽略。下面通过OkHttp来实现图片的上传

代码

直接上代码UploadFileHelper.kt

object UploadFileHelper {    //--------ContentType    private val MEDIA_OBJECT_STREAM = MediaType.parse("multipart/form-data")    //--------上传延时时间    private val WRITE_TIME_OUT:Long  = 50    private val mOkHttpClient by lazy { OkHttpClient() }    //------基本参数----------    val version = AppConstant.API_VERSION    val platform = AppConstant.API_PLATFORM    val methodName = AppConstant.API_UPLOADFILE_METHOD    val token = ignoreException("") { UserModel.token() }    val userId = ignoreException(0) { UserModel.id() }    //------------------------    //不带参数同步上传文件    fun syncUploadFile(actionUrl: String = "",file: File,maxW: Int = 256,maxH: Int = 256):String?{        val uploadFile = optionFileSize(file,maxW,maxH,null)        if(uploadFile!=null){            val response = createNoParamsOkHttpCall(actionUrl,uploadFile).execute()            if(uploadFile.exists())                uploadFile.delete()            return getResponseToPath(response.body()!!.string())        }        return null    }    //不带参数异步上传文件    fun asyncUploadFile(actionUrl:String = "", file: File,maxW: Int = 256,maxH: Int = 256,                        uploadCallBackListener: UploadCallBackListener? = null){        val uploadFile = optionFileSize(file,maxW,maxH,uploadCallBackListener)        if(uploadFile!=null)        createNoParamsOkHttpCall(actionUrl,uploadFile).enqueue(object: Callback{            override fun onFailure(c: Call, e: IOException) {                uploadCallBackListener?.onUploadFailure(e.toString())            }            override fun onResponse(c: Call, response: Response) {                if(uploadFile.exists())                uploadFile.delete()                 uploadCallBackListener?.onUploadSuccess(getResponseToPath(response.body()!!.string()))                response.body()!!.close()            }        })    }    //带参数同步上传文件    fun syncParamsUploadFile(actionUrl: String= "",file: File,params:HashMap<String,Any>,                       maxW: Int = 256,maxH: Int = 256):String?{        val uploadFile = optionFileSize(file,maxW,maxH,null)        if(uploadFile!=null){            params.put("filename",uploadFile)            val response = createParamsOkHttpCall(actionUrl,params,null,false).execute()            if(uploadFile.exists())                uploadFile.delete()            return getResponseToPath(response.body()!!.string())        }        return null    }    //带参数异步上传文件    fun asyncParamsUploadFile(actionUrl: String= "",file: File,params:HashMap<String,Any>,maxW: Int = 256,maxH: Int = 256,                        uploadCallBackListener: UploadCallBackListener? = null, isProgress:Boolean = true){        val uploadFile = optionFileSize(file,maxW,maxH,uploadCallBackListener)        if(uploadFile!=null){            params.put("filename",uploadFile)            createParamsOkHttpCall(actionUrl,params,uploadCallBackListener,isProgress).enqueue(object :Callback{                override fun onFailure(c: Call, e: IOException) {                    uploadCallBackListener?.onUploadFailure(e.toString())                }                override fun onResponse(c: Call, response: Response) {                      if(uploadFile.exists())                        uploadFile.delete()                    uploadCallBackListener?.onUploadSuccess(getResponseToPath(response.body()!!.string()))                    response.body()!!.close()                }            })        }    }    //------创建一个没有带参数的Call    fun createNoParamsOkHttpCall(actionUrl: String,file: File):Call{        val requestUrl = "${AppConstant.HOST}/$actionUrl"        val requestBody = RequestBody.create(MEDIA_OBJECT_STREAM,file)        val request = Request.Builder().url(requestUrl).post(requestBody).build()        return mOkHttpClient.newBuilder().writeTimeout(WRITE_TIME_OUT,TimeUnit.SECONDS).build().newCall(request)    }    //------创建一个带参数的Call    fun createParamsOkHttpCall(actionUrl: String,params:Map<String,Any>,                               uploadCallBackListener: UploadCallBackListener? = null,                               isProgress:Boolean = true):Call{        //-----AppConstant.HOST 上传图片的Server的BASE_URL http://xxx.com        val requestUrl = "${AppConstant.HOST}/$actionUrl"        val builder = MultipartBody.Builder()        builder.setType(MultipartBody.FORM)        val newParams = mutableMapOf(                "version" to version,                "platform" to platform,                "methodName" to methodName,                "token" to token,                "user_id" to userId)        newParams.putAll(params)        newParams.forEach( action = {            if(it.value is File){                builder.addFormDataPart(it.key, (it.value as File).name,                if(isProgress) createProgressRequestBody(MEDIA_OBJECT_STREAM!!,(it.value as File),uploadCallBackListener)                else RequestBody.create(null, (it.value as File)))            }else{                builder.addFormDataPart(it.key,it.value.toString())            }        })        val body = builder.build()        val request = Request.Builder().url(requestUrl).post(body).build()        return mOkHttpClient.newBuilder().writeTimeout(WRITE_TIME_OUT,TimeUnit.SECONDS).build().newCall(request)    }    //创建带进度RequestBody    fun createProgressRequestBody(contentType:MediaType,file:File,                                 uploadCallBackListener: UploadCallBackListener? = null):RequestBody{        return object:RequestBody(){            override fun contentType(): MediaType = contentType            override fun contentLength() = file.length()            override fun writeTo(sink: BufferedSink) {                ignoreException {                    val source = Okio.source(file)                    val buf = Buffer()                    val remaining = contentLength()                    var current: Long = 0                    var readCount: Long = source.read(buf, 2048)                    while (readCount != -1L) {                        sink.write(buf, readCount)                        current += readCount                        uploadCallBackListener?.onUploadProgress(current,remaining)                        readCount = source.read(buf, 2048)                    }                }            }        }    }    //根据图片大小简单压缩    fun optionFileSize(file: File,maxW:Int,maxH:Int,uploadCallBackListener: UploadCallBackListener?):File?{        try {            val uploadFile = File(AppBridge.AppContext().externalCacheDir, file.hashCode().toString())            ImageUtils.resize(file, maxW, maxH, uploadFile)            return uploadFile        } catch (e: Exception) {            uploadCallBackListener?.onUploadFailure("压缩图片失败")            return null        }    }   //解析Server返回的数据获取图片路径,    /*       {"code":200,"msg":"上传成功","data":{"path":""}}   */  fun getResponseToPath(response:String):String{        val dataJsonObj = JSONObject(response).get("data") as JSONObject        return dataJsonObj.get("path") as String    }    //回调方法    interface UploadCallBackListener{        fun onUploadFailure(error:String)        fun onUploadProgress(currentSize:Long,totalSize:Long)        fun onUploadSuccess(path:String)    }}
inline fun <T> ignoreException(def: T, f: () -> T): T {    try {        return f()    } catch(e: Exception) {        Timber.e(e, "")        return def    }}

最后根据是否要带参数、同步或异步调用其中对应的方法可以了

syncUploadFile(xxx)asyncUploadFile(xxx)syncParamsUploadFile(xxx)asyncParamsUploadFile(xxx)

总结

  • 首先根据是否要带参数上传,如果不带参数上传,直接创建RequestBody;如果带参数上传,创建MultipartBody.Builder(),然后把所有参数addFormDataPart进去,其中addFormDataPart方法有个RequestBody参数通过是否要监听进度创建,如果需要进度,需重写RequestBodywriteTo()方法,如果不监听进度,直接创建RequestBody,最后builder.build()得到RequestBody
  • 通过上步骤得到的RequestBody以及上传图片的Server路径,可以配置出一个Request对象。

  • Request对象通过.newCall(request)配置在OkHttpClient得到Call对象

  • 最后Call调用同步.execute()或者异步.enqueue(callBack),在回调里面处理返回的数据。