Android 10+如何为MediaStore openOutputStream设置Long类型偏移量?
解决Android 10+中MediaStore大文件下载的int偏移量限制问题
首先要澄清一个关键误解:你提到的write(buffer, 0, byteSize)中的第二个参数是字节数组内部的起始偏移,不是文件的全局写入偏移量。文件的写入位置是由OutputStream自动维护的——每次写入后,流的指针会自动后移对应字节数,完全不需要手动指定文件的全局偏移。
为什么int参数不会限制2GB以上大文件?
int的最大值是2^31-1(约2.1GB),但这并不意味着不能下载超过2GB的文件。实际开发中,我们只需要控制单次写入的字节数在int范围内即可(比如常用的4KB、1MB、4MB缓冲区),通过多次分块写入来完成大文件的下载,累计的总字节数可以用long类型跟踪,不会出现溢出问题。
优化后的代码实现
下面是针对大文件下载优化后的代码,重点优化了流的安全管理、分块写入逻辑,并加入了总字节数跟踪:
val resolver = contentResolver val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, "CuteKitten001") put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") put(MediaStore.MediaColumns.RELATIVE_PATH, "Download/PerracoLabs") } val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) uri?.let { targetUri -> // 使用use函数自动管理OutputStream的关闭,避免资源泄漏 resolver.openOutputStream(targetUri)?.use { outputStream -> var totalBytesWritten: Long = 0 hiper.get("https://httpbin.org/image", isStream = true) .ifException { e -> Log.d(TAG, "Exception: ${e?.message}") } .ifFailed { response -> Log.d(TAG, "Failed") } .ifStream { buffer, byteSize -> buffer?.let { // 确保byteSize在int范围内(实际hiper返回的buffer块大小不会超过int上限) outputStream.write(it, 0, byteSize) totalBytesWritten += byteSize Log.d(TAG, "Written $totalBytesWritten bytes so far") } ?: run { outputStream.flush() Log.d(TAG, "Done. Total bytes: $totalBytesWritten") } } .finally { response -> Log.d(TAG, response.text.toString()) } } ?: run { Log.d(TAG, "Failed to open output stream") } } ?: run { Log.d(TAG, "Failed to create media store entry") }
关键注意事项
- 流的安全关闭:使用Kotlin的
use函数可以自动关闭OutputStream,避免因异常导致的资源泄漏。 - 缓冲区大小控制:确保单次写入的
byteSize始终在int范围内——大多数网络库返回的缓冲区块大小(比如8KB、16KB)都远小于int的上限,所以不需要额外处理。 - 权限检查:在Android 10+中,使用
MediaStore下载到公共目录不需要WRITE_EXTERNAL_STORAGE权限,但如果是Android 9及以下版本,仍需申请该权限。 - 异常处理:建议在写入过程中加入更多异常捕获逻辑(比如IO异常),避免因写入失败导致的文件损坏。
内容的提问来源于stack exchange,提问作者Jeeva
相关产品推荐
相关产品推荐

