文件存储概要
文件存储包括媒体存储,文本存储。媒体存储比如图片,视频,音频等。
文存储比如,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) {
}
})
758

被折叠的 条评论
为什么被折叠?



