Xamarin.Android实现将对象列表写入设备下载存储CSV文件的方法
在C# Xamarin.Android中导出对象列表为带表头的CSV文件(存储到下载目录)
嘿,这个需求我熟!咱们一步步来实现,保证导出的CSV带表头、每行一个对象,还能稳稳存到设备的下载目录里。我会把权限处理、CSV格式构建、文件写入这些关键点都讲清楚,避免踩坑。
1. 先搞定存储权限
Android对外部存储的访问有严格的权限控制,尤其是6.0(API 23)及以上版本需要动态申请权限,10+版本还得适配Scoped Storage。
首先在AndroidManifest.xml里声明必要权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Android 10+ 访问公共下载目录额外需要的权限 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" />
然后在代码里动态申请权限:
private async Task<bool> CheckAndRequestPermissions() { var writePerm = ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage); var readPerm = ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage); var permissionsToAsk = new List<string>(); if (writePerm != Permission.Granted) permissionsToAsk.Add(Manifest.Permission.WriteExternalStorage); if (readPerm != Permission.Granted) permissionsToAsk.Add(Manifest.Permission.ReadExternalStorage); // Android 10+ 额外处理全局存储权限 if (Build.VERSION.SdkInt >= BuildVersionCodes.R && !Environment.IsExternalStorageManager) { permissionsToAsk.Add(Manifest.Permission.ManageExternalStorage); } if (permissionsToAsk.Any()) { var result = await ActivityCompat.RequestPermissionsAsync(this, permissionsToAsk.ToArray()); return result.All(r => r.Value == Permission.Granted); } return true; }
2. 定义你的对象类
假设你要导出的是Person对象列表,先定义这个类(换成你自己的业务对象就行):
public class Person { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public string Email { get; set; } }
3. 构建CSV内容(带表头+处理特殊字符)
要特别注意CSV的特殊字符处理! 如果字段里包含逗号、引号或者换行,直接写会导致格式错乱,必须用双引号包裹,内部的双引号还要转成两个双引号。
写个工具方法处理这个,再生成完整的CSV内容:
// 处理CSV字段的特殊字符 private string EscapeCsvValue(string value) { if (string.IsNullOrEmpty(value)) return string.Empty; // 如果包含逗号、引号、换行,就用双引号包裹,内部双引号转义 if (value.Contains(",") || value.Contains("\"") || value.Contains("\n") || value.Contains("\r")) { return $"\"{value.Replace("\"", "\"\"")}\""; } return value; } // 生成带表头的CSV内容 private string GenerateCsvContent(List<Person> persons) { // 表头用对象的属性名 var headers = new List<string> { nameof(Person.Id), nameof(Person.Name), nameof(Person.Age), nameof(Person.Email) }; var csvBuilder = new StringBuilder(); // 添加表头行 csvBuilder.AppendLine(string.Join(",", headers)); // 逐行添加对象数据 foreach (var person in persons) { var values = new List<string> { person.Id.ToString(), EscapeCsvValue(person.Name), person.Age.ToString(), EscapeCsvValue(person.Email) }; csvBuilder.AppendLine(string.Join(",", values)); } return csvBuilder.ToString(); }
4. 写入CSV到下载目录
不同Android版本的存储策略不一样,我们要做版本适配:Android 9及以下可以直接操作文件,Android 10+推荐用MediaStore写入公共下载目录,避免Scoped Storage限制。
private async Task<bool> SaveCsvToDownloads(string csvContent, string fileName = "data.csv") { try { if (Build.VERSION.SdkInt >= BuildVersionCodes.Q) { // Android 10+ 使用MediaStore写入公共下载目录 var contentValues = new ContentValues(); contentValues.Put(MediaStore.MediaColumns.DisplayName, fileName); contentValues.Put(MediaStore.MediaColumns.MimeType, "text/csv"); contentValues.Put(MediaStore.MediaColumns.RelativePath, Environment.DirectoryDownloads); var uri = ContentResolver.Insert(MediaStore.Downloads.ExternalContentUri, contentValues); if (uri == null) return false; using (var outputStream = ContentResolver.OpenOutputStream(uri)) using (var writer = new StreamWriter(outputStream)) { await writer.WriteAsync(csvContent); } } else { // Android 9及以下直接操作文件 var downloadsDir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads); var csvFile = new File(downloadsDir, fileName); using (var writer = new StreamWriter(csvFile)) { await writer.WriteAsync(csvContent); } } Toast.MakeText(this, "CSV已成功导出到下载目录", ToastLength.Short).Show(); return true; } catch (Exception ex) { Toast.MakeText(this, $"导出失败:{ex.Message}", ToastLength.Short).Show(); return false; } }
5. 整合起来使用
在你的Activity里,比如按钮点击事件中调用这些方法:
private async void ExportBtn_Click(object sender, EventArgs e) { // 先检查权限 var hasPermission = await CheckAndRequestPermissions(); if (!hasPermission) { Toast.MakeText(this, "需要存储权限才能导出CSV", ToastLength.Short).Show(); return; } // 模拟你的业务对象列表 var personList = new List<Person> { new Person { Id = 1, Name = "张三", Age = 25, Email = "zhangsan@example.com" }, new Person { Id = 2, Name = "李四, 带逗号", Age = 30, Email = "lisi@example.com" }, new Person { Id = 3, Name = "王五\"带引号\"", Age = 28, Email = "wangwu@example.com" } }; // 生成CSV内容 var csvContent = GenerateCsvContent(personList); // 保存到下载目录 await SaveCsvToDownloads(csvContent); }
这样导出的CSV就会和你参考的示例一样,带表头、每行一个对象,还能在设备的下载文件夹里找到它!
内容的提问来源于stack exchange,提问作者Chitu
相关产品推荐
相关产品推荐

