C#解压文件后执行文件操作触发UnauthorizedAccessException原因排查
问题背景
将用户选择文件解压的逻辑从ViewModel移到中央WGFileOperations类后,执行File.ReadLines(unZippedFilePath)时触发UnauthorizedAccessException,此前在ViewModel中运行正常。
工作流程:ViewModel调用WGFileOperations.UnzipFile()选择压缩日志文件,解压后返回路径,随后尝试读取该路径下的文件时抛出权限异常。
相关代码
ViewModel代码
private void ProcessLogFile(object parameter) { string unZippedFilePath = WGFileOperations.UnzipFile(); var lines = File.ReadLines(unZippedFilePath); // 此处触发UnauthorizedAccessException foreach (var lineCheck in lines) { /* 业务逻辑 */ } // 省略其他代码 }
WGFileOperations.UnzipFile()代码
public static string UnzipFile() { OpenFileDialog openFileDialog = new OpenFileDialog(); string _filePath = null; if (openFileDialog.ShowDialog() == true) { _filePath = openFileDialog.FileName; } SaveFileDialog saveFileDialog = new SaveFileDialog(); saveFileDialog.FileName = Path.GetFileNameWithoutExtension(_filePath); saveFileDialog.RestoreDirectory = true; string extractPath = @"C:\Orica\UnpackedFiles\" + _filePath; // 默认解压路径 if (saveFileDialog.ShowDialog() == true) { extractPath = saveFileDialog.FileName; } if (File.Exists(extractPath) == false) { try { ZipFile.ExtractToDirectory(_filePath, extractPath); MessageBox.Show("File Created Ok I Guess Because I Didn't Throw An Exception!"); } catch (Exception ex) { MessageBox.Show("Error attempting to extract file! " + ex); } } else { MessageBox.Show("That file already exists"); } return extractPath; }
原因分析
默认路径拼接错误:
@"C:\Orica\UnpackedFiles\" + _filePath会生成非法路径,比如原文件路径是C:\Logs\test.zip,拼接后变成C:\Orica\UnpackedFiles\C:\Logs\test.zip,这种格式的路径系统无法识别,后续操作会触发权限或路径不存在的异常。ZipFile.ExtractToDirectory参数误用:该方法的第二个参数是解压目标目录,而非文件路径。但代码中用SaveFileDialog获取文件名作为目标,相当于试图把压缩包内容解压到一个"文件"中,系统会拒绝这种操作,直接抛出权限异常。未处理用户取消操作:如果用户取消
OpenFileDialog或SaveFileDialog,_filePath或extractPath会是无效值,后续读取操作自然会出错。
解决方案
1. 修正默认解压路径
使用Path.Combine拼接合法路径,只取原文件的文件名作为子目录:
// 替换原默认路径代码 string extractDir = Path.Combine(@"C:\Orica\UnpackedFiles", Path.GetFileNameWithoutExtension(_filePath));
2. 替换SaveFileDialog为FolderBrowserDialog
因为ZipFile.ExtractToDirectory需要目标目录,而非文件,改用文件夹选择对话框让用户指定解压位置:
// 替换原SaveFileDialog相关代码 FolderBrowserDialog folderDialog = new FolderBrowserDialog(); folderDialog.SelectedPath = @"C:\Orica\UnpackedFiles"; string extractDir = Path.Combine(@"C:\Orica\UnpackedFiles", Path.GetFileNameWithoutExtension(_filePath)); if (folderDialog.ShowDialog() == true) { extractDir = Path.Combine(folderDialog.SelectedPath, Path.GetFileNameWithoutExtension(_filePath)); } // 检查目录是否存在,而非文件 if (!Directory.Exists(extractDir)) { try { ZipFile.ExtractToDirectory(_filePath, extractDir); MessageBox.Show("文件解压完成!"); } catch (Exception ex) { MessageBox.Show("解压错误:" + ex.Message); } } else { MessageBox.Show("解压目录已存在"); } // 假设压缩包内只有一个日志文件,返回具体文件路径 return Directory.GetFiles(extractDir, "*.log").FirstOrDefault();
3. 处理用户取消操作
在对话框调用后增加校验,避免无效路径传递到后续逻辑:
// OpenFileDialog校验 if (openFileDialog.ShowDialog() != true) { MessageBox.Show("未选择压缩文件"); return null; } _filePath = openFileDialog.FileName; // ViewModel中增加空值校验 private void ProcessLogFile(object parameter) { string unZippedFilePath = WGFileOperations.UnzipFile(); if (string.IsNullOrEmpty(unZippedFilePath)) { return; } var lines = File.ReadLines(unZippedFilePath); // 后续逻辑 }
4. 确保目录权限
- 避免使用系统盘根目录下的自定义文件夹(如
C:\Orica),改用用户有权限的目录,比如Environment.SpecialFolder.MyDocuments:string baseDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Orica", "UnpackedFiles"); string extractDir = Path.Combine(baseDir, Path.GetFileNameWithoutExtension(_filePath)); - 必要时以管理员身份运行程序,或给目标目录添加当前用户的读写权限。
内容的提问来源于stack exchange,提问作者NondescriptChap

