HoloLens2部署Unity应用时CsvHelper保存CSV的路径获取方案
HoloLens2端CsvHelper写入CSV文件的路径适配方案
问题背景
- 开发环境为Unity,目标平台HoloLens2,需求是使用CsvHelper生成存储手部运动检测数据的CSV文件,文件名自动按采集手类型(左手/右手)、写入时间拼接生成
- 编辑器下远程调试时,硬编码本地PC磁盘路径的写入逻辑可正常运行;将应用部署到HoloLens2设备端后,硬编码的PC路径完全失效
- 由于文件为程序自动命名、且CsvHelper写入逻辑需要直接传入可写路径/流,无法每次写入都通过文件选择器交互指定路径,此前未找到HoloLens2端可直接使用的路径规则
初始版本硬编码PC路径的代码如下:
/// <summary> /// Method <c>WriteInCsv</c> writes the records in a .csv file. Files are named by corresponding hand, and the dateTime of writing. /// </summary> /// <param name="records">List of all the saved data</param> /// <param name="hand">Which hand has the data been recorded from : 0 for right hand, 1 for left hand. </param> public void WriteInCsv(List<CoordCSV> records, int hand=0) { if (records.Count > 0) { string path = "C:\\Users\\AT04760\\Documents\\2022_HOLOLENS\\CSV Export\\test2_"; //硬编码的PC本地路径,设备端不可用 string dateTime = System.DateTime.Now.Day + "-" + System.DateTime.Now.Month + "_" + System.DateTime.Now.Hour + "-" + System.DateTime.Now.Minute + "-" + System.DateTime.Now.Second; if (hand == 1) { path += "LeftHand_" + dateTime + ".csv"; } else { path += "RightHand_" + dateTime + ".csv"; } using (var writer = new StreamWriter(path)) using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)) { csv.Context.RegisterClassMap<CoordMap>(); csv.WriteRecords(records); } } }
可运行适配方案
核心逻辑:HoloLens2跑的是UWP系统,不支持像PC那样直接硬编码盘符路径,得用UWP原生的Windows.Storage接口拿合法可写目录:第一次运行弹个文件夹选择器让用户选一次存数据的地方,把目录访问权限存在FutureAccessList里缓存住,之后再运行不用重复选,直接拿到目录的可写流就能按自动生成的文件名写入,完全兼容原来用的StreamWriter和CsvHelper。
适配后的可运行代码如下:
public void WriteInCsv(List<CoordCSV> records, string folderName, int hand=0) { if (records.Count > 0) { string fileName = "test_"; string dateTime = System.DateTime.Now.Day + "-" + System.DateTime.Now.Month + "_" + System.DateTime.Now.Hour + "-" + System.DateTime.Now.Minute + "-" + System.DateTime.Now.Second; if (hand == 1) { fileName += "LeftHand_" + dateTime + ".csv"; } else { fileName += "RightHand_" + dateTime + ".csv"; } // 若要继续使用CsvHelper,无需手动拼接CSV行,拿到流后直接传入CsvWriter即可 List<string> lines = new List<string>(); lines.Add("Finger,TimeStamp,Position,Rotation(Quaternion)"); foreach (CoordCSV data in records) { lines.Add(data.Finger +"," + data.Timestamp + "," + data.Position + "," + data.Rotation); } #if !UNITY_EDITOR && UNITY_WSA_10_0 // UWP存储相关API必须在UI线程调用 UnityEngine.WSA.Application.InvokeOnUIThread(async () => { Windows.Storage.StorageFolder folder; // 检查是否已经缓存过目标目录的访问权限 if(Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.ContainsItem(folderName)){ folder = await Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.GetFolderAsync(folderName); } else{ // 无缓存权限时弹出文件夹选择器,让用户指定一次存储目录 var folderPicker = new Windows.Storage.Pickers.FolderPicker(); folderPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary; folderPicker.FileTypeFilter.Add("*"); folder = await folderPicker.PickSingleFolderAsync(); } // 切回应用线程执行写入逻辑 UnityEngine.WSA.Application.InvokeOnAppThread(async () => { if (folder != null) { // 缓存目录权限,后续启动无需重复选择 Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.AddOrReplace(folderName, folder); // 打开文件流,无需手动拼接绝对路径 using (StreamWriter writer = new StreamWriter(await folder.OpenStreamForWriteAsync(fileName, CreationCollisionOption.OpenIfExists))) { foreach(string line in lines){ await writer.WriteLineAsync(line); } // 若使用CsvHelper,替换上面的逐行写入逻辑即可: // using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)) // { // csv.Context.RegisterClassMap<CoordMap>(); // csv.WriteRecords(records); // } } } }, false); }, false); #endif // 编辑器下可保留原有PC路径写入逻辑,方便调试 #if UNITY_EDITOR string editorPath = "C:\\Users\\AT04760\\Documents\\2022_HOLOLENS\\CSV Export\\" + fileName; using (var writer = new StreamWriter(editorPath)) using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)) { csv.Context.RegisterClassMap<CoordMap>(); csv.WriteRecords(records); } #endif } }
注意事项
- 首次在HoloLens2端运行时,系统会弹出文件夹选择界面,选一个可访问的存储位置就行(比如图片库、本地存储里自己建的文件夹),选过一次之后后续写入不会再弹选择框
- 别硬写HoloLens2的系统绝对路径,UWP应用有权限沙箱,直接访问未授权的路径必抛权限错误
- 线程调度的代码不能省:UWP的文件选择器、存储相关API必须在UI线程调用,不然会抛线程异常
内容的提问来源于stack exchange,提问作者Sashell
相关产品推荐
相关产品推荐

