You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Google Drive API备份恢复Android应用数据异常问题求助

问题描述

我正在使用Google Drive API实现类似WhatsApp的Android应用数据备份功能:将多个文件(如数据库、其他杂项文件)写入单个二进制文件后上传至Google Drive。但下载该文件进行恢复时,出现两个问题:

  • 文件末尾的字节与上传时不一致
  • 读取第三个文件的size值时得到异常大的数值,进而引发OutOfMemoryError异常

相关代码

备份代码

backupFile.outputStream().use { outputStream ->
            // Write File 1 data
            file1.inputStream().use {
                val data = it.readBytes()
                outputStream.write(data.size)
                outputStream.write(data)
            }

            // Write File 2 data
            file2.inputStream().use {
                val data = it.readBytes()
                outputStream.write(data.size))
                outputStream.write(data)
            }

            // Write File 3 data
            file3.inputStream().use {
                val data = it.readBytes()
                outputStream.write(data.size)
                outputStream.write(data)
            }
        }

恢复代码

dataInputStream.use { inputStream ->
            // Read File 1 Data
            file1.outputStream().use {
                val size = inputStream.read()
                val data = ByteArray(size)
                inputStream.read(data)
                it.write(data)
            }

            // Read File 2 Data
            file2.outputStream().use {
                val size = inputStream.read()
                val data = ByteArray(size)
                inputStream.read(data)
                it.write(data)
            }

            // Read File 3 Data
            file3.outputStream().use {
                val size = inputStream.read()
                val data = ByteArray(size)
                inputStream.read(data)
                it.write(data)
            }
        }

上传代码

suspend fun uploadBackupFile(file: File) = withContext(Dispatchers.IO) {
        val gFile = com.google.api.services.drive.model.File().apply {
            name = file.name
            parents = backupParents // String With Drive Spaces
        }
        val fileContent = FileContent(BACKUP_MIME_TYPE, file) // MIME TYPE is application/octet-stream

        val createdFile = getDriveService().Files().create(gFile, fileContent).execute()

下载代码

suspend fun downloadBackupFile(fileId: String): InputStream = withContext(Dispatchers.IO) {
        getDriveService()
            .Files()
            .get(fileId)
            .executeMediaAsInputStream()
    }
问题原因
  1. 文件大小读写不匹配
    备份时调用outputStream.write(data.size),这个方法仅写入1个字节(有效范围0-255),但data.size是Int类型(占4个字节)。如果文件大小超过255,写入的只是Int值的最低8位;恢复时inputStream.read()也只能读取1个字节,导致获取的size和真实文件大小完全不符。前面的文件读取偏移错误后,第三个文件的size会读到文件数据的字节,变成超大值,直接触发OutOfMemoryError。

  2. InputStream.read()读取不完整
    恢复时用inputStream.read(data)无法保证一次性填满整个字节数组,该方法返回实际读取的字节数。如果流(尤其是网络下载的InputStream)出现分段读取,会导致写入的文件数据缺失,最终文件末尾字节和原文件不一致。

修复方案

修正备份代码(写入完整的文件大小)

使用DataOutputStream写入完整的4字节Int类型,确保文件大小被正确记录:

backupFile.outputStream().use { outputStream ->
    DataOutputStream(outputStream).use { dataOut ->
        // Write File 1 data
        file1.inputStream().use {
            val data = it.readBytes()
            dataOut.writeInt(data.size) // 写入完整4字节Int
            dataOut.write(data)
        }

        // Write File 2 data
        file2.inputStream().use {
            val data = it.readBytes()
            dataOut.writeInt(data.size)
            dataOut.write(data)
        }

        // Write File 3 data
        file3.inputStream().use {
            val data = it.readBytes()
            dataOut.writeInt(data.size)
            dataOut.write(data)
        }
    }
}

修正恢复代码(读取完整的文件大小与数据)

用DataInputStream读取完整的Int值,并循环读取确保数据完整:

DataInputStream(dataInputStream).use { dataIn ->
    // Read File 1 Data
    file1.outputStream().use { out ->
        val size = dataIn.readInt() // 读取4字节Int
        val data = ByteArray(size)
        var bytesRead = 0
        // 循环读取直到填满整个数组
        while (bytesRead < size) {
            val read = dataIn.read(data, bytesRead, size - bytesRead)
            if (read == -1) break // 处理流提前结束的异常情况
            bytesRead += read
        }
        out.write(data)
    }

    // Read File 2 Data
    file2.outputStream().use { out ->
        val size = dataIn.readInt()
        val data = ByteArray(size)
        var bytesRead = 0
        while (bytesRead < size) {
            val read = dataIn.read(data, bytesRead, size - bytesRead)
            if (read == -1) break
            bytesRead += read
        }
        out.write(data)
    }

    // Read File 3 Data
    file3.outputStream().use { out ->
        val size = dataIn.readInt()
        val data = ByteArray(size)
        var bytesRead = 0
        while (bytesRead < size) {
            val read = dataIn.read(data, bytesRead, size - bytesRead)
            if (read == -1) break
            bytesRead += read
        }
        out.write(data)
    }
}

额外优化(避免大文件内存溢出)

备份时不要用readBytes()一次性读取整个文件到内存,大文件会直接触发OOM,建议分块读写:

// 分块读取写入示例(替换备份代码中的readBytes部分)
val buffer = ByteArray(8192) // 8KB缓冲区
file1.inputStream().use { inStream ->
    val dataSize = file1.length().toInt()
    dataOut.writeInt(dataSize)
    var bytesRead: Int
    while (inStream.read(buffer).also { bytesRead = it } != -1) {
        dataOut.write(buffer, 0, bytesRead)
    }
}

内容的提问来源于stack exchange,提问作者Remijius Brian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 12:42:09