最近在项目中遇到使用到FTP上传和下载资源,特此记录一下实现的方式。Android实现FTP的功能主要用到了Apache的Commons Net库,将Commons Net的jar包下载引入到项目中即可。
基本使用流程图如下:
FTP协议和HTTP协议有所不同,使用FTP进行下载时,你需要进行登录 *** 作。如果服务器没设置登录功能可忽略登录 *** 作。
private fun ftpConnect( host: String, port: Int, username: String?, password: String?, enterLocalPassiveMode:Boolean = true ): FTPClient { val ftpClient = FTPClient() try { //设置超时时间以毫秒为单位使用时,从数据连接读。 ftpClient.defaultTimeout = 10000 ftpClient.connectTimeout = 10000 ftpClient.setDataTimeout(10000) Log.d(TAG, "connecting to the ftp server $host:$port") //连接到FTP服务器 ftpClient.connect(host, port) ftpClient.login(username, password) //是否开启被动模式 if (enterLocalPassiveMode) { ftpClient.isRemoteVerificationEnabled = false ftpClient.enterLocalPassiveMode() } //请求使用UTF-8编码 ftpClient.controlEncoding = "utf-8" val reply: Int = ftpClient.replyCode if (!FTPReply.isPositiveCompletion(reply)) { ftpClient.disconnect() Log.e(TAG, "无法连接到ftp服务器,错误码为:$reply") } else { Log.d(TAG, "连接到ftp服务器") } } catch (e: Exception) { e.printStackTrace() Log.e(TAG, "Error: could not connect to host $host") } return ftpClient }
注意:由于FTP服务器默认的编码是ISO-8859-1,因此,客户端在获取文件信息时需要请求服务器使用UTF-8编码(如果服务器支持的话),如果服务器不支持开启UTF-8编码,那么客户端在请求remotePath路径、获取文件名时,都需要对路径进行编码转换处理。
2.2 断开连接private fun ftpDisconnect(ftpClient: FTPClient?) { // 判断空指针 if (ftpClient == null) { return } // 断开ftp服务器连接 try { ftpClient.logout() ftpClient.disconnect() Log.d(TAG, "断开连接") } catch (e: Exception) { Log.e(TAG, "Error occurred while disconnecting from ftp server.") } }
注意:在下载或上传资源时首先要连接服务器,在完成 *** 作后要记得断开连接。
2.3 上传文件fun uploadFile( host: String, port: Int, username: String?, password: String?, ftpSavePath: String, ftpSaveFileName: String, inputStream: InputStream ): Boolean { val ftpClient = ftpConnect(host, port, username, password) var flag = false try { val pathName = if (ftpSavePath.endsWith("/")) "$ftpSavePath$ftpSaveFileName" else "$ftpSavePath/$ftpSaveFileName" Log.i( TAG, """ 上传文件的路径 :$pathName 开始上传文件... """.trimIndent() ) //设置文件类型 ftpClient.setFileType(FTP.BINARY_FILE_TYPE) flag = ftpClient.storeFile(String(pathName.toByteArray(), Charsets.ISO_8859_1),inputStream) inputStream.close() } catch (e: Exception) { Log.e(TAG, e.toString()) } finally { Log.i(TAG, "$ftpSaveFileName 文件上传 :" + if (flag) "成功" else "失败 ") try { inputStream.close() } catch (e: IOException) { Log.e(TAG, e.toString()) } finally { ftpDisconnect(ftpClient) } } return flag }
注意:如果服务器不支持UTF-8编码,则要按上面的String(pathName.toByteArray(), Charsets.ISO_8859_1)的写法,进行编码转换处理,不然中文会乱码。如果支持UTF-8编码,则直接使用中文字符。
2.4 下载文件fun downloadFile( host: String, port: Int, username: String?, password: String?, pathName: String, localPath: String ): Boolean { val ftpClient = ftpConnect(host, port, username, password) var flag = false var outputStream: OutputStream? = null try { Log.i( TAG, """ 下载文件的路径 :$pathName 开始下载文件... """.trimIndent() ) ftpClient.setFileType(FTP.BINARY_FILE_TYPE) createLocalDirectory(localPath) outputStream = FileOutputStream(File(localPath)) // 下载文件 flag = ftpClient.retrieveFile( String(pathName.toByteArray(), Charsets.ISO_8859_1), outputStream ) outputStream.close() } catch (e: Exception) { Log.e(TAG, e.toString()) } finally { Log.i(TAG, "$pathName 文件 :" + if (flag) "成功" else "失败 ") if (null != outputStream) { try { outputStream.close() } catch (e: IOException) { Log.e(TAG, e.toString()) } finally { ftpDisconnect(ftpClient) } } } return flag } //创建本地多层目录文件,如果已存在该文件,则不创建,如果无,则创建 @Throws(IOException::class) private fun createLocalDirectory(localPath: String) { val path = localPath.substringBeforeLast("/") val file = File(path) if (!file.exists()) { file.mkdirs() } }三、其他 *** 作
为了安全,服务器可能只给了上传和下载的权限,也有可能给了其他 *** 作权限,如删除文件、新建目录等。
3.1 创建目录@Throws(IOException::class) fun createDirectory( host: String, port: Int, username: String?, password: String?, remote: String ): Boolean { val ftpClient = ftpConnect(host, port, username, password) var flag = true val directory = remote.substring(0, remote.lastIndexOf("/") + 1) if (!directory.equals("/", ignoreCase = true) && !ftpClient.changeWorkingDirectory(String(directory.toByteArray(), Charsets.ISO_8859_1)) ) { // 如果远程目录不存在,则递归创建远程服务器目录 var start = 0 var end = 0 start = if (directory.startsWith("/")) { 1 } else { 0 } end = directory.indexOf("/", start) while (true) { val subDirectory = String( remote.substring(start, end).toByteArray(), Charsets.ISO_8859_1 ) if (!ftpClient.changeWorkingDirectory(subDirectory)) { if (ftpClient.makeDirectory(subDirectory)) { ftpClient.changeWorkingDirectory(subDirectory) } else { flag = false } } start = end + 1 end = directory.indexOf("/", start) // 检查所有目录是否创建完毕 if (end <= start) { break } } } Log.i(TAG, "$remote 文件创建 :" + if (flag) "成功" else "失败 ") ftpDisconnect(ftpClient) return flag }3.2 删除文件
fun deleteFile( host: String, port: Int, username: String?, password: String?, serverPath: String ): Boolean { val ftpClient = ftpConnect(host, port, username, password) val flag = deleteFile(ftpClient, serverPath) Log.i(TAG, "$serverPath 文件删除 :" + if (flag) "成功" else "失败 ") ftpDisconnect(ftpClient) return flag } private fun deleteFile(ftpClient: FTPClient, pathName: String): Boolean { try { val files = ftpClient.listFiles(pathName) if (files.isNotEmpty()) { for (file in files) { if (file.isDirectory) { deleteFile(ftpClient, pathName + "/" + file.name) // 切换到父目录,不然删不掉文件夹 ftpClient.changeWorkingDirectory( pathName.substring( 0, pathName.lastIndexOf("/") ) ) ftpClient.removeDirectory(pathName) } else { if (!ftpClient.deleteFile(pathName + "/" + file.name)) { return false } } } } // 切换到父目录,不然删不掉文件夹 ftpClient.changeWorkingDirectory(pathName.substring(0, pathName.lastIndexOf("/"))) ftpClient.removeDirectory(pathName) } catch (e: IOException) { e.printStackTrace() } return true }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)