Xamarin.Android复制Assets内容到内部存储时Open文件报错解决
Xamarin.Android Assets文件复制异常问题排查与解决
我需要将Xamarin.Android项目中Assets目录下html/Model1/Content文件夹内的10余个文件,复制到应用数据文件夹下新建的Models文件夹中,编写了如下异步复制代码,但运行时在assetManager.Open(filename)处触发异常,第一个文件0.pf就出现该问题:
private async Task copyFileAsync() { var outputName = Application.Context.GetExternalFilesDir("").AbsolutePath + "/Models"; if (!System.IO.File.Exists (outputName)) { Directory.CreateDirectory(outputName); } AssetManager assetManager = this.Assets; String[] files = null; try { files = assetManager.List("html/Model1/Content"); } catch (System.IO.IOException e) { System.Console.WriteLine("tag", "Failed to get asset file list.", e); } foreach (String filename in files) { string filePath = System.IO.Path.Combine(outputName, filename); using (var fileAssetStream = assetManager.Open(filename)) using (var fileStream = new FileStream (filePath, FileMode.OpenOrCreate)) { await copyInToLocationAsync(fileAssetStream, fileStream, filePath); } System.Console.WriteLine("------COPIED------------------------"); } } private async Task copyInToLocationAsync(Stream fileAssetStream, Stream fileStream, string path) { byte[] buffer = new byte[1024]; int b = buffer.Length; int length; while ((length = await fileAssetStream.ReadAsync(buffer, 0, b)) > 0) { await fileStream.WriteAsync(buffer, 0, length); } fileStream.Flush(); fileStream.Close(); fileAssetStream.Close(); }
异常原因分析
- Asset路径错误:
assetManager.List("html/Model1/Content")获取到的是该子目录下的纯文件名,但assetManager.Open(filename)时没有带上完整相对路径,AssetManager会默认在Assets根目录查找文件,自然找不到目标文件,抛出不存在异常。 - 目录判断逻辑错误:用
File.Exists(outputName)判断目录是否存在,这是用来检测文件的方法,应该用Directory.Exists,否则目录已存在时会执行无效的创建操作。 - 冗余流操作:
copyInToLocationAsync中手动调用Close(),但外层using语句会自动释放流资源,手动关闭属于冗余操作。
解决方法与修正代码
- 修正Asset文件路径:打开文件时使用完整相对路径,通过
Path.Combine拼接目录和文件名。 - 修复目录存在判断:替换
File.Exists为Directory.Exists。 - 移除冗余流关闭代码:依赖
using自动管理流资源。
修正后的完整代码:
private async Task CopyFileAsync() { // 用Path.Combine拼接路径,避免字符串拼接的潜在问题 var outputDir = Path.Combine(Application.Context.GetExternalFilesDir("").AbsolutePath, "Models"); if (!Directory.Exists(outputDir)) { Directory.CreateDirectory(outputDir); } AssetManager assetManager = this.Assets; string[] files = null; try { files = assetManager.List("html/Model1/Content"); } catch (IOException e) { Console.WriteLine("获取Asset文件列表失败:", e); } // 空值判断,避免空数组遍历 if (files == null || files.Length == 0) return; foreach (string filename in files) { string filePath = Path.Combine(outputDir, filename); // 使用完整的Asset相对路径打开文件 using (var fileAssetStream = assetManager.Open(Path.Combine("html/Model1/Content", filename))) using (var fileStream = new FileStream(filePath, FileMode.OpenOrCreate)) { await CopyIntoLocationAsync(fileAssetStream, fileStream); } Console.WriteLine($"已复制文件:{filename}"); } } private async Task CopyIntoLocationAsync(Stream fileAssetStream, Stream fileStream) { byte[] buffer = new byte[1024]; int length; while ((length = await fileAssetStream.ReadAsync(buffer, 0, buffer.Length)) > 0) { await fileStream.WriteAsync(buffer, 0, length); } // using块会自动处理流的Flush和释放,无需手动操作 }
内容的提问来源于stack exchange,提问作者Randi
相关产品推荐
相关产品推荐

