使用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() }
问题原因
文件大小读写不匹配
备份时调用outputStream.write(data.size),这个方法仅写入1个字节(有效范围0-255),但data.size是Int类型(占4个字节)。如果文件大小超过255,写入的只是Int值的最低8位;恢复时inputStream.read()也只能读取1个字节,导致获取的size和真实文件大小完全不符。前面的文件读取偏移错误后,第三个文件的size会读到文件数据的字节,变成超大值,直接触发OutOfMemoryError。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
相关产品推荐
相关产品推荐

