使用OkHttp上传图片

简介

上传图片是一个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),在回调里面处理返回的数据。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容