package com.duolebo.blyrobot.data import android.content.Context import android.util.Log import com.duolebo.blyrobot.tools.FtpManager import com.duolebo.blyrobot.util.AppUtil import com.duolebo.blyrobot.util.Config import net.gotev.uploadservice.* import net.gotev.uploadservice.ftp.FTPUploadRequest import net.gotev.uploadservice.ftp.UnixPermissions import java.io.File import java.util.* import kotlin.collections.ArrayList /** * 图片上传任务 */ class ImageUploadTask { companion object { const val TAG = "RobotTaskUpload" } var packageName: String = "" var uploadImages = ArrayList() var uploadId: String = "" var reUpload = false var isRunning = false private var context: Context private var uploadCheckTimer: Timer? = null private var isUploadChecking = false private var checkDelay = 5 * 60 * 1000L private var checkCount = 0 constructor(context: Context) { this.context = context } fun from(task: Task) { this.packageName = task.apkInfo.packageName this.uploadImages.clear() this.uploadImages.addAll(task.uploadImages) } fun needReUpload(): Boolean { if (this.isRunning) return false if (!this.reUpload) return false if (isUploadRunning()) return false if (this.isUploadChecking) return false return true } // 任务是否在上传队列中 private fun isUploadRunning(): Boolean { val runningTaskIds = UploadService.getTaskList() if (this.uploadId in runningTaskIds) return true return false } fun start() { if (this.uploadImages.isEmpty()) { uploadTaskListener?.onComplete() return } this.isRunning = true Thread { uploadImage() }.start() // 预计每张图片上传时间是3秒 startUploadCheck(3 * 1000L * this.uploadImages.size) } // 由于ftp库存在bug,这里启动一个定时器进行超时上传检测 private fun startUploadCheck(delay: Long) { Log.i(TAG, "startUploadCheck with delay: $delay") this.checkDelay = delay uploadCheckTimer?.cancel() if (this.checkCount >= 3) { Log.i(TAG, "check time out, quit this job") uploadTaskListener?.onComplete() return } uploadCheckTimer = Timer() uploadCheckTimer!!.schedule(object : TimerTask() { override fun run() { checkUpload() } }, delay) this.checkCount++ } private fun checkUpload() { Log.i(TAG, "check image upload") if (isUploadRunning()) { startUploadCheck(this.checkDelay / 3) return } this.isUploadChecking = true val lastUploadPos = getLastUploadImagePos() Log.i(TAG, "get last upload pos:$lastUploadPos") val size = this.uploadImages.size // 移除所有已经上传的图片 val reUploadImages = this.uploadImages.subList(lastUploadPos, size - 1) this.uploadImages.removeAll(reUploadImages) this.reUpload = true this.isUploadChecking = false } // 通过检查url是否存在找到最后上传图片的位置 private fun getLastUploadImagePos(): Int { val res: Int val size = this.uploadImages.size var checkPos = size / 2 var halfPos = checkPos while (true) { val exist = AppUtil.existsUrl(this.uploadImages[checkPos]) if (exist) { checkPos += halfPos / 2 } else { checkPos -= halfPos / 2 } halfPos /= 2 if (halfPos == 0) { res = if (exist) checkPos else checkPos - 1 break } } return res } private fun uploadImage() { Log.i(TAG, "upload image....") try { val uploadRequest = FTPUploadRequest(context, Config.instance.getFtpServer(), 21) .setUsernameAndPassword(Config.instance.getFtpUserName(), Config.instance.getFtpPassword()) .setNotificationConfig(UploadNotificationConfig()) .setCreatedDirectoriesPermissions(UnixPermissions("777")) .setSocketTimeout(15000) .setConnectTimeout(15000) .setDelegate(uploadImageCallback()) .setMaxRetries(4) val unExistFiles = ArrayList() this.uploadImages.forEach { val uploadFile = File(it) if (uploadFile.exists()) { uploadRequest.addFileToUpload(it, Config.instance.getFtpRemotePath() + uploadFile.name) } else { unExistFiles.add(it) } } // 删除不存在的图片记录 if (!unExistFiles.isEmpty()) { this.uploadImages.removeAll(unExistFiles) } this.uploadId = uploadRequest.startUpload() Log.i(TAG, "upload id $uploadId") } catch (exc: Exception) { Log.e(TAG, exc.message, exc) } } private fun uploadComplete(errorCount: Int) { Log.i(TAG, "upload errorCount: $errorCount") uploadCheckTimer?.cancel() this.isRunning = false if (errorCount > 0 && errorCount > this.uploadImages.size / 3) { this.reUpload = true } else { // 上传完毕直接删除问题 this.uploadImages.forEach { val uploadFile = File(it) if (uploadFile.exists()) { uploadFile.delete() } } } uploadTaskListener?.onComplete() } /** * ftp 上传回调监听 */ private fun uploadImageCallback(): UploadStatusDelegate { val totalUpload = this.uploadImages.size Log.i(TAG, "total upload count : $totalUpload") return object : UploadStatusDelegate { override fun onCancelled(context: Context?, uploadInfo: UploadInfo?) { // empty } override fun onProgress(context: Context?, uploadInfo: UploadInfo?) { // empty } override fun onError(context: Context?, uploadInfo: UploadInfo?, serverResponse: ServerResponse?, exception: java.lang.Exception?) { val uploadSize = uploadInfo?.successfullyUploadedFiles!!.size Log.i(TAG, "onError...uploadCount: $uploadSize") } override fun onCompleted(context: Context?, uploadInfo: UploadInfo?, serverResponse: ServerResponse?) { val uploadSize = uploadInfo?.successfullyUploadedFiles!!.size Log.i(TAG, "onCompleted...uploadCount: $uploadSize") uploadComplete(totalUpload - uploadSize) } } } var uploadTaskListener: OnUploadTaskListener? = null /** * 图片上传任务状态 */ interface OnUploadTaskListener { fun onComplete() } }