【文件存储二】

文件存储概要

文件存储包括媒体存储,文本存储。媒体存储比如图片,视频,音频等。
文存储比如,PDF,Word,txt等等其他不属于媒体存储的存储。
关于媒体存储请参考其他博客, mermaid语法说明
当然文件存储是通用能力,也可以包含媒体存储。
这里以一个小demo展示存储流程。

大概需求

我们存储本地输入框或者服务端返回的数据,以Word的格式存储在本地。

权限检测

我们需要在Activity类中声明权限的ActivityResultLauncher:

class PermissionActivity : AppCompatActivity() {
    private val storagePermissionRequest = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) {
            convertBase64StringToWordAndStore()
        } else {
            Toast.makeText(this@PermissionActivity, "存储权限被拒绝,请到系统设置页打开", Toast.LENGTH_SHORT).show()
        }
    }

我们需要在存储之前先检查权限:

private fun checkStoragePermission() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {  //TIRAMISU
            // 低于 Android 13 不需要检查存储权限
            val permission = ContextCompat.checkSelfPermission(this@PermissionActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
            if (permission) {
                convertBase64StringToWordAndStore()
            } else {
                storagePermissionRequest.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
            }
        } else {
            startCreateDocumentIntent()
        }
    }

数据获取

这里简单演示从输入框获取数据:

mWordContent = Base64.encodeToString(editTv.text.toString().toByteArray(), Base64.DEFAULT)

mWordContent建议也设置全局变量,类型为String,并且需要Base64编码一下。

存储相关

首先需要声明一个全局变量:

private val createDocumentLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            val uri: Uri? = result.data?.data
            uri?.let {
                saveWordFileToUri(it)
            }
        }
    }

然后在存储的地方启动文件创建意图:

// 启动文件创建意图
    private fun startCreateDocumentIntent() {
        val currentDateTime = DateFormat.getDateTimeInstance().format(Date())
        val fileName = "demo_$currentDateTime.docx"
        val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
            addCategory(Intent.CATEGORY_OPENABLE)
            type = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
            putExtra(Intent.EXTRA_TITLE, fileName)
        }
        createDocumentLauncher.launch(intent)
    }

之后就会触发saveWordFileToUri(it)方法:

private fun saveWordFileToUri(uri: Uri) {
        try {
            contentResolver.openOutputStream(uri)?.use { outputStream ->
                writeWordContent(outputStream, mWordContent)
                Toast.makeText(this, "文件保存成功", Toast.LENGTH_SHORT).show()
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Toast.makeText(this, "文件保存失败", Toast.LENGTH_SHORT).show()
        }
    }

    private fun writeWordContent(outputStream: OutputStream, content: String) {
        // 先解码 Base64 字符串为字节数组
        val decodedBytes = Base64.decode(
            mWordContent.replaceFirst("^data:.*;base64,".toRegex(), ""),
            Base64.DEFAULT
        )
        outputStream.write(decodedBytes)
        outputStream.flush()
    }

有时候数据可能是服务端返回的

声明如下:

private var contentInputStream: InputStream? = null
    private val createDocumentLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            val uri: Uri? = result.data?.data
            uri?.let {
                contentInputStream?.let { inputStream ->
                    try {
                        val outputStream = contentResolver.openOutputStream(it)
                        inputStream.copyTo(outputStream!!)
                        outputStream?.close()
                        inputStream.close()
                        Toast.makeText(this@ImagePreviewActivity, getString(R.string.photo_preview_save_success), Toast.LENGTH_SHORT).show()
                    } catch (e: IOException) {
                        e.printStackTrace()
                        Toast.makeText(this@ImagePreviewActivity, getString(R.string.photo_preview_save_failure), Toast.LENGTH_SHORT).show()
                    }
                }
            }
        }
    }

假设这里有个网络请求:

val uploadService = RetrofitManager.getInstance().createFileUploadService()
            val call = uploadService.downloadImage(imageUrl!!)
            call.enqueue(object : Callback<ResponseBody> {
                override fun onResponse(
                    call: Call<ResponseBody>,
                    response: Response<ResponseBody>
                ) {
                    if (response.isSuccessful) {
                        response.body()?.byteStream()?.use { inputStream: InputStream ->
                            contentInputStream = inputStream
                            createDocumentLauncher.launch(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
                                addCategory(Intent.CATEGORY_OPENABLE)
                                type = "image/jpeg"
                                putExtra(Intent.EXTRA_TITLE, "图片_${System.currentTimeMillis()}.jpg")
                            })
                        }
                    }
                }

                override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
                }
            })
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值