我目前正在 Android 应用程序中开发一项新功能,允许用户将数据导出为 CSV 文件。
导出过程在后端管理。在应用程序端,我们下载二进制文件,暂时保存,然后使用 ACTION_VIEW 意图进行预览。这允许用户选择他们喜欢的应用程序来查看和共享文件。
但是,我遇到了编码问题,但仅限于使用表格应用程序时。在 PC、macOS 甚至 Google Sheets Web 上打开文件可以按预期工作。
我们获取文档如下:
...
override suspend fun getDocument(file: File): File {
val body = http.execute(
call = { service.getDocument(...) },
onError = { ... },
)
withContext(dispatchers.io) {
file.writeBytes(body.source().readByteArray())
}
return file
}
...
然后我们使用意图打开它:
...
fun createFileIntent(context: Context, file: File, mediaType: MimeType? = null): Intent {
val contentUri = file.getContentUri(context)
val type = mediaType?.value ?: contentUri.getMimeType(context)
val intent = Intent(ACTION_VIEW)
intent.setDataAndType(contentUri, type)
intent.flags = FLAG_GRANT_READ_URI_PERMISSION
return intent
}
...
private fun showCsv(file: File) {
val intent = createFileIntent(requireContext(), file)
startActivity(intent)
}
Response.ok(outputStream(activitiesCSV))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"activities.csv\"")
.header(HttpHeaders.CONTENT_TYPE, "text/csv; charset=utf-8")
.build()
private StreamingOutput outputStream(byte[] bytes) {
final byte[] bom = new byte[] { (byte) 239, (byte) 187, (byte) 191 }; //UTF-8 BOM
return outputStream -> {
outputStream.write(bom);
outputStream.write(bytes);
outputStream.flush();
};
}
activitiesCSV
是从数据库检索的 byte[]
。
如果我不添加 UTF-8-BOM
,在 MS Excel 中打开 csv 时将无法识别特殊字符,但这里我们在 Google Sheets 应用程序上遇到了问题。
我们通过删除后端的 BOM 标头并在 Android 端添加以下代码解决了该问题,这确保了从 UTF-8 到 ISO_8859_1 (Latin 1) 的转换。
override suspend fun getDocument(file: File): File {
val body = http.execute(
call = { service.getDocument(...) },
onError = { ... },
)
withContext(dispatchers.io) {
val sourceByteArray = body.source().readByteArray()
val byteArrayToWrite = when (fileType) {
CSV ->
sourceByteArray
.toString(body.contentType()?.charset() ?: UTF_8)
.toByteArray(ISO_8859_1)
else -> sourceByteArray
}
file.writeBytes(byteArrayToWrite)
}
return file
}