比起云服务器、云数据库、云存储等等方式将文件存储至云端,网盘的WebDAV协议对新手就友好的多,不仅仅有免部署使用方式简单等等原因,更重要是免费,国内首推坚果云网盘!

        三个注意点:

  • 使用第三方库Sardine进行操作
  • 添加网络访问权限
  • 子线程进行云端请求

特别说明:

        本文采用分—总结构展示代码(除gradle文件),即先展示局部代码,并说明其用法和作用;小节结尾处再给出该部分对应文件的完整代码布局XML源文件在文末。

一、添加依赖

        在gradle(Moudle)中添加第三方库Sardine所需依赖。

// 添加sardine库来使用WebDAV协议
implementation 'com.thegrizzlylabs.sardine-android:sardine-android:0.8'
implementation("com.squareup.okhttp3:okhttp:4.9.0")

二、声明网络权限

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.demo_webdav">
    // 额外添加网络权限
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        ...
    </application>
</manifest>

 三、创建操作网盘的方法

        Sardine操作WebDAV协议的常用方法请参考常用方法一览

3.1初始化连接,返回一个可共用的Sardine对象

// 与坚果云WebDAV服务器建立连接,返回sardine对象以进行操作
fun initSardine(): OkHttpSardine {
    val sardine = OkHttpSardine()
    // 此处的账号和第三方密码口令均请填入自己的
    // 坚果云的账号
    val userName = "demo_webdav@163.com"
    // 授权给第三方应用的密码口令
    val passWord = "aqkgubiy4z55rc6p"
    // 建立连接
    sardine.setCredentials(userName, passWord)
    // 返回sardine对象
    return sardine
}

        在坚果云生成第三方应用密码口令的方式请参考坚果云官方文档

3.2新建文件夹/目录至云端

fun createDir(sardine: OkHttpSardine) {
    val dirPath = "https://dav.jianguoyun.com/dav/我的坚果云/demo_webdav文件夹"
    sardine.createDirectory(dirPath)
}

 3.3检查云端文件存在性

fun checkExistence(sardine: OkHttpSardine): Boolean {
    val dirPath = "https://dav.jianguoyun.com/dav/我的坚果云/demo_webdav文件夹"
    return sardine.exists(dirPath)
}

3.4上传文件至云端:将String变量转换为byte字节数组上传至网盘 

fun uploadFile(sardine: OkHttpSardine, fileContent: String) {
    val filePath = "https://dav.jianguoyun.com/dav/我的坚果云/demo_webdav文件夹/测试文本.txt"
    // 将变量转变为byte字节数组,以传输到网盘
    val data = fileContent.toByteArray()
    sardine.put(filePath, data)
}

3.5下载云端文件方法:以输入流的形式读取,并转换为String变量存储

fun downloadFile(sardine: OkHttpSardine): String {
    val filePath = "https://dav.jianguoyun.com/dav/我的坚果云/demo_webdav文件夹/测试文本.txt"
    val download = sardine.get(filePath)
    // 以输入流的形式读取下载的文件,并转换为字符串
    val fileContent = BufferedReader(InputStreamReader(download)).useLines { lines ->
        val results = StringBuilder()
        lines.forEach {
            results.append(it)
        }
        results.toString()
    }
    return fileContent
}

3.6重命名或移动文件(夹)的方法:

fun moveOrRenameFile(sardine: OkHttpSardine) {
    val oldPath = "https://dav.jianguoyun.com/dav/我的坚果云/demo_webdav文件夹"
    val newPath = "https://dav.jianguoyun.com/dav/我的坚果云/renamed_or_moved_demo_webdav文件夹"
    sardine.move(oldPath, newPath)
}

3.7删除云端文件(夹)方法: 

fun deleteFile(sardine: OkHttpSardine) {
    val filePath = "https://dav.jianguoyun.com/dav/我的坚果云/moved_demo_webdav文件夹"
    sardine.delete(filePath)
}

四、MainActivity中调用

4.1布局预览

 4.2绑定视图控件

val show = findViewById<TextView>(R.id.show)
val createButton = findViewById<Button>(R.id.create)
val uploadButton = findViewById<Button>(R.id.upload)
val downloadButton = findViewById<Button>(R.id.download)
val renameButton = findViewById<Button>(R.id.rename)
val deleteButton = findViewById<Button>(R.id.delete)

 4.3设置点击事件及真机测试

        注:与网络请求有关的函数,需要放到子线程中进行!

// 启动一个子线程
thread {
    // 在这内部写真正实现功能的函数
}

        1.创建文件夹按钮

createButton.setOnClickListener {
    // 启动子线程
    thread {
        // 连接到坚果云WebDAV服务器,并返回sardine对象
        val sardine = initSardine()
        // 文件夹不存在则创建一个
        if (!checkExistence(sardine)) {
            createDir(sardine)
        }
    }
}


        2.上传文件按钮

uploadButton.setOnClickListener {
    // 启动子线程
    thread {
        val sardine = initSardine()
        uploadFile(sardine, "demo_webdav的测试文本,这将生成为一个txt文本存在WebDAV服务器端")
    }
    // Toast一下提醒已上传
    Toast.makeText(this, "已上传", Toast.LENGTH_SHORT).show()
}


3.下载文件按钮

downloadButton.setOnClickListener {
    var fileContent = ""
    // 启动子线程
    thread {
        val sardine = initSardine()
        fileContent = downloadFile(sardine)
    }.join()  // 阻塞子线程,以取得fileContent的值
    // 将fileContent的值赋予show文本控件显示出来
    show.text = fileContent
}

       


 4.(移动)重命名文件夹按钮

renameButton.setOnClickListener {
    // 启动子线程
    thread {
        val sardine = initSardine()
        moveOrRenameFile(sardine)
    }
    // Toast一下提醒已重命名
    Toast.makeText(this, "已重命名", Toast.LENGTH_SHORT).show()
}

      


  5.删除文件夹按钮

deleteButton.setOnClickListener {
    // 启动子线程
    thread {
        val sardine = initSardine()
        deleteFile(sardine)
    }
    // Toast一下提醒已删除
    Toast.makeText(this, "已删除", Toast.LENGTH_SHORT).show()
}


         MainActivity完整代码

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 绑定视图控件
        val show = findViewById<TextView>(R.id.show)
        val createButton = findViewById<Button>(R.id.create)
        val uploadButton = findViewById<Button>(R.id.upload)
        val downloadButton = findViewById<Button>(R.id.download)
        val renameButton = findViewById<Button>(R.id.rename)
        val deleteButton = findViewById<Button>(R.id.delete)

        // 设置创建文件夹按钮的点击事件
        createButton.setOnClickListener {
            // 启动子线程
            thread {
                // 连接到坚果云WebDAV服务器,并返回sardine对象
                val sardine = initSardine()
                // 文件夹不存在则创建一个
                if (!checkExistence(sardine)) {
                    createDir(sardine)
                }
            }
        }

        // 设置上传文件按钮的点击事件
        uploadButton.setOnClickListener {
            // 启动子线程
            thread {
                val sardine = initSardine()
                uploadFile(sardine, "demo_webdav的测试文本,这将生成为一个txt文本存在WebDAV服务器端")
            }
            // Toast一下提醒已上传
            Toast.makeText(this, "已上传", Toast.LENGTH_SHORT).show()
        }

        // 设置下载文件按钮的点击事件
        downloadButton.setOnClickListener {
            var fileContent = ""
            // 启动子线程
            thread {
                val sardine = initSardine()
                fileContent = downloadFile(sardine)
            }.join()  // 阻塞子线程,以取得fileContent的值
            // 将fileContent的值赋予show文本控件显示出来
            show.text = fileContent
        }

        // 设置重命名文件夹按钮的点击事件
        renameButton.setOnClickListener {
            // 启动子线程
            thread {
                val sardine = initSardine()
                moveOrRenameFile(sardine)
            }
            // Toast一下提醒已重命名
            Toast.makeText(this, "已重命名", Toast.LENGTH_SHORT).show()
        }

        // 设置删除文件夹按钮的点击事件
        deleteButton.setOnClickListener {
            // 启动子线程
            thread {
                val sardine = initSardine()
                deleteFile(sardine)
            }
            // Toast一下提醒已删除
            Toast.makeText(this, "已删除", Toast.LENGTH_SHORT).show()
        }
    }

    // 与坚果云WebDAV服务器建立连接,返回sardine对象以进行操作
    fun initSardine(): OkHttpSardine {
        val sardine = OkHttpSardine()
        // 坚果云的账号邮箱
        val userName = "demo_webdav@163.com"
        // 授权给第三方应用的密码口令
        val passWord = "aqkgubiy4z55rc6p"
        // 建立连接
        sardine.setCredentials(userName, passWord)
        // 返回sardine对象
        return sardine
    }

    fun createDir(sardine: OkHttpSardine) {
        val dirPath = "https://dav.jianguoyun.com/dav/我的坚果云/demo_webdav文件夹"
        sardine.createDirectory(dirPath)
    }

    fun checkExistence(sardine: OkHttpSardine): Boolean {
        val dirPath = "https://dav.jianguoyun.com/dav/我的坚果云/demo_webdav文件夹"
        return sardine.exists(dirPath)
    }

    fun uploadFile(sardine: OkHttpSardine, fileContent: String) {
        val filePath = "https://dav.jianguoyun.com/dav/我的坚果云/demo_webdav文件夹/测试文本.txt"
        // 将变量转变为byte字节数组,以传输到网盘
        val data = fileContent.toByteArray()
        sardine.put(filePath, data)
    }

    fun downloadFile(sardine: OkHttpSardine): String {
        val filePath = "https://dav.jianguoyun.com/dav/我的坚果云/demo_webdav文件夹/测试文本.txt"
        val download = sardine.get(filePath)
        // 以文件流的形式读取下载的文件,并转换为字符串
        val fileContent = BufferedReader(InputStreamReader(download)).useLines { lines ->
            val results = StringBuilder()
            lines.forEach {
                results.append(it)
            }
            results.toString()
        }
        return fileContent
    }

    fun moveOrRenameFile(sardine: OkHttpSardine) {
        val oldPath = "https://dav.jianguoyun.com/dav/我的坚果云/demo_webdav文件夹"
        val newPath = "https://dav.jianguoyun.com/dav/我的坚果云/renamed_or_moved_demo_webdav文件夹"
        sardine.move(oldPath, newPath)
    }

    fun deleteFile(sardine: OkHttpSardine) {
        val filePath = "https://dav.jianguoyun.com/dav/我的坚果云/renamed_or_moved_demo_webdav文件夹"
        sardine.delete(filePath)
    }
}

        布局XML完整代码

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.39" />

    <Button
        android:id="@+id/create"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:text="创建文件夹"
        app:layout_constraintBottom_toTopOf="@+id/upload"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/show" />

    <Button
        android:id="@+id/upload"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="上传文件"
        app:layout_constraintBottom_toTopOf="@+id/download"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/create" />

    <Button
        android:id="@+id/download"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="下载文件"
        app:layout_constraintBottom_toTopOf="@+id/rename"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/upload" />

    <Button
        android:id="@+id/rename"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="重命名文件夹"
        app:layout_constraintBottom_toTopOf="@+id/delete"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/download" />

    <Button
        android:id="@+id/delete"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="删除文件夹"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/rename" />

</androidx.constraintlayout.widget.ConstraintLayout>

如有疏忽遗漏之处,敬请指出

本文demo源码  github、gitee地址:

https://github.com/darlingxyz/demo_webdav

https://gitee.com/darlingxyz/demo_webdav

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐