Android 常用文件操作方法

Android 常用文件操作方法

文件压缩、md5计算

package io.github.okhttplearn.ui.screen

import android.content.Context
import android.os.Environment
import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.github.okhttplearn.ui.theme.OkhttpLearnTheme
import io.github.okhttplearn.utils.Utils
import okio.buffer
import okio.sink
import java.io.File

private const val TAG: String = "WorldScreen"

@Composable
internal fun WorldScreen(
    modifier: Modifier = Modifier,
    snackBarHostState: SnackbarHostState,
) {
    val context: Context = LocalContext.current
    val srcFile: File = remember {
        File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "src.txt")
    }
    val destFile: File = remember {
        File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "dest.gz")
    }
    Column(modifier = modifier) {
        Text(
            text = "写文件", modifier = Modifier
                .fillMaxWidth()
                .padding(all = 5.dp)
                .background(
                    color = Color(
                        color = 0xFFF8BBD0
                    ),
                    shape = RoundedCornerShape(size = 5.dp)
                )
                .padding(all = 5.dp)
                .clickable {
                    Log.i(TAG, "WorldScreen -> srcFile: $srcFile")
                    if (srcFile.parentFile?.exists() == false) {
                        Log.i(TAG, "WorldScreen -> isSuccess: ${srcFile.parentFile?.mkdirs()}")
                    }
                    srcFile.sink().buffer().use { bufferedSink ->
                        for (i in 0 until 1000) {
                            bufferedSink.writeUtf8("Hello World: $i")
                        }
                    }
                }
        )
        Text(
            text = "压缩zip", modifier = Modifier
                .fillMaxWidth()
                .padding(all = 5.dp)
                .background(
                    color = Color(
                        color = 0xFFF8BBD0
                    ),
                    shape = RoundedCornerShape(size = 5.dp)
                )
                .padding(all = 5.dp)
                .clickable {
                    Log.i(TAG, "WorldScreen -> srcFile: $srcFile, destFile: $destFile")
                    Utils.compressFileToGzip2(srcFile, destFile)
                }
        )
        Text(
            text = "压缩gzip", modifier = Modifier
                .fillMaxWidth()
                .padding(all = 5.dp)
                .background(
                    color = Color(
                        color = 0xFFF8BBD0
                    ),
                    shape = RoundedCornerShape(size = 5.dp)
                )
                .padding(all = 5.dp)
        )
        Text(
            text = "计算md5", modifier = Modifier
                .fillMaxWidth()
                .padding(all = 5.dp)
                .background(
                    color = Color(
                        color = 0xFFF8BBD0
                    ),
                    shape = RoundedCornerShape(size = 5.dp)
                )
                .padding(all = 5.dp)
                .clickable {
                    val md5: String = Utils.getFileMd5(destFile)
                    Log.i(TAG, "WorldScreen -> md5: $md5")
                }
        )
    }
}

@Preview(showBackground = true)
@Composable
private fun WorldScreenPreview() {
    OkhttpLearnTheme {
        val snackBarHostState: SnackbarHostState = remember { SnackbarHostState() }
        WorldScreen(modifier = Modifier, snackBarHostState = snackBarHostState)
    }
}

工具方法

package io.github.okhttplearn.utils

import okio.BufferedSink
import okio.BufferedSource
import okio.buffer
import okio.gzip
import okio.sink
import okio.source
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.io.InputStream
import java.security.MessageDigest
import java.util.zip.GZIPOutputStream

private const val TAG: String = "Utils"

internal object Utils {

    internal fun compressFileToGzip1(sourceFile: File, targetFile: File) {
        targetFile.outputStream().buffered().use { fileOutputStream: BufferedOutputStream ->
            GZIPOutputStream(fileOutputStream).buffered()
                .use { gzipOutputStream: BufferedOutputStream ->
                    sourceFile.inputStream().buffered()
                        .use { bufferedInputStream: BufferedInputStream ->
                            bufferedInputStream.copyTo(out = gzipOutputStream)
                        }
                }
        }
    }

    /**
     * ```shell
     * PS C:\Users\29051> (Get-FileHash -Path C:\Users\29051\Downloads\dest.gz -Algorithm MD5).Hash.ToLower()
     * 98cbb6b41e3a4aeec9e35dfec9351673
     * ```
     */
    internal fun compressFileToGzip2(sourceFile: File, targetFile: File) {
        targetFile.sink().gzip().buffer().use { bufferedSink: BufferedSink ->
            sourceFile.source().buffer().use { bufferedSource: BufferedSource ->
                bufferedSource.readAll(sink = bufferedSink)
            }
        }
    }

    internal fun getFileMd5(file: File): String {
        val digest: MessageDigest = MessageDigest.getInstance("MD5")
        file.inputStream().buffered().use { inputStream: InputStream ->
            val bytes = ByteArray(1024 * 8)
            var length: Int
            while (inputStream.read(bytes).also { length = it } > 0) {
                digest.update(bytes, 0, length)
            }
            val md5Bytes: ByteArray = digest.digest()
            return md5Bytes.joinToString(separator = "") { "%02x".format(it) }
        }
    }

	internal fun unzip(zipFile: File, targetDir: File){
		if (!targetDir.isDirectory) {
			throw RuntimeException("目标目录不能是文件")
		}
		ZipFile(zipFile).use { zipFile: ZipFile ->
			zipFile.entries().asSequence()
				.forEach { entry: ZipEntry ->
					val entryFile = File(targetDir, entry.name)
					if (entry.isDirectory){
						val isSuccess: Boolean = entryFile.mkdirs()
						Log.i(TAG, "unzip -> 目录是否创建成功: $isSuccess")
					} else {
						val parentFile: File? = entryFile.parentFile
						if (parentFile?.exists() == false){
							val isSuccess: Boolean = parentFile.mkdirs()
							Log.i(TAG, "unzip -> 父目录创建是否成功: $isSuccess")
						}
						zipFile.getInputStream(entry).source().buffer().use { bufferedSource: BufferedSource ->
							entryFile.sink().buffer().use { bufferedSink: BufferedSink ->
								bufferedSink.writeAll(source = bufferedSource)
							}
						}
					}
				}
		}
	}
internal fun zip(srcDir: File, zipFile: File){
        if (!srcDir.isDirectory){
            throw RuntimeException("源路径必须是一个目录: ${srcDir.absolutePath}")
        }
        val dirs = ArrayDeque<File>(initialCapacity = 256)
        dirs.add(srcDir)
        ZipOutputStream(zipFile.sink().buffer().outputStream()).use { zipOutputStream: ZipOutputStream ->
            while(dirs.isNotEmpty()){
                val node: File = dirs.removeFirst()
                Log.i(TAG, "zip -> directory: $node")
                node.listFiles()?.forEach { file ->
                    if (file.isDirectory){
                        dirs.add(file)
                        val zipEntry = ZipEntry("${file.toRelativeString(base = srcDir)}${File.separator}") // 命名参数
                        Log.i(TAG, "zip -> isDirectory zipEntryName: ${zipEntry.name}")
                        zipOutputStream.putNextEntry(zipEntry)
                        zipOutputStream.closeEntry()
                    } else {
                        Log.i(TAG, "zip -> file: $file")
                        val zipEntry = ZipEntry(file.toRelativeString(base = srcDir))
                        Log.i(TAG, "zip -> file zipEntryName: ${zipEntry.name}")
                        zipOutputStream.putNextEntry(zipEntry)
                        file.source().buffer().use { bufferedSource: BufferedSource ->
                            // zipOutputStream.write(bufferedSource.readByteArray())
                            val bytes = ByteArray(size = 1024 * 8)
                            var length: Int
                            while(bufferedSource.read(sink = bytes).also { length = it } > 0){
                                zipOutputStream.write(bytes, 0, length)
                            }
                        }
                        zipOutputStream.closeEntry()
                    }
                }
            }
        }
    }
	
}

文件zip压缩调用方法

val zipFile = File(
	Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
	"mysql-connector-c++-9.5.0-src"
)
Utils.zip(zipFile, File(zipFile.parentFile!!, "mysql-connector-c++-9.5.0-src.zip"))

zip文件压缩可以看我的这篇文章

zip文件压缩

还有的话会继续在这篇文章补充

posted @ 2025-11-22 11:20  爱情丶眨眼而去  阅读(4)  评论(0)    收藏  举报