目錄
問題案例
環境:
電腦A:winform程序;
電腦B:部署了一個文件上傳的WCF服務在IIS上。且該服務的配置文件中已經增加
<identity impersonate="true" password="1234" userName="Test" /> //該賬戶同時存在於電腦B和電腦C中
;
電腦C:公布的共享路徑;
在A的winform程序連接B的WCF服務上傳文件,B的WCF服務將文件保存至C的共享路徑中。
結果:路徑無法訪問。
System.UnauthorizedAccessException: 對路徑“”的訪問被拒絕
對比:同樣的操作使用wevservice和.aspx都可以正常訪問共享路徑並操作。
期望/最終目的:找到可以在WCF中訪問共享路徑的解決方案。
原因分析
排除:權限不足的原因
因為其他兩種方式(WebService和.aspx)在沒有配置【Asp.net模擬】的身份驗證方式之前,也是同樣的錯誤。使用【Asp.net模擬】的身份驗證后,就可以正常操作。
所以我在想是不是因為WCF未能調用【Asp.net模擬】的身份驗證?或者說該項配置對WCF無效?
================2014-10-20==============
排除:服務引用方式
嘗試使用 Web References 的方式添加WCF服務的引用(之前是Service References )。問題依舊。
解決問題
方案:在WCF服務的靜態構造函數中,通過調用net use 命令實現路徑映射,從而實現訪問。
示例代碼如下:
1 public class FileService : IFileService 2 { 3 /// <summary> 4 /// FileService的靜態構造函數 5 /// </summary> 6 static FileService() 7 { 8 string shareName="\\192.168.1.2\shares"; 9 //用戶名勿比指定共享服務器的IP或名稱,否則會引起1312錯誤 10 string user="192.168.1.2\Test"; 11 string pwd="123"; 12 NetUseHelper.Build(shareName, user, pwd, string.Empty);//不指定盤符,避免引起盤符被占用的錯誤 13 /* 14 *不建議指定盤符。因為IIS的網站默認是在IUser賬戶下運行的,而映射只針對'建立時的賬戶'有效。所以IUser下的映射又是無法預知的。 15 所以應該建立連接后,仍然使用共享地址訪問文件或目錄。 16 */ 17 } 18 19 } 20 21 /// <summary> 22 /// net use 建立映射的功能模塊 23 /// </summary> 24 public static class NetUseHelper 25 { 26 /// <summary> 27 /// 所有支持的驅動器號 28 /// </summary> 29 static readonly string[] driveNames = { "ZYXWVUTSRQPONMLKJIHGFEDC" }; 30 /// <summary> 31 /// 建立映射 32 /// </summary> 33 /// <param name="sharename">共享路徑</param> 34 /// <param name="user">用戶名。為 null 不指定用戶 35 /// <para>請務必使用共享服務器的IP或PC名稱+用戶名,例如:192.168.1.1\User。否則可能引起1312錯誤</para> 36 /// </param> 37 /// <param name="password">密碼。為 null 不指定密碼</param> 38 /// <param name="devicename">磁盤驅動器名稱,例如(C:)。為 null 自動分配驅動器號 。為 空 不指定驅動器號</param> 39 /// <returns>驅動器名稱</returns> 40 public static string Build(string sharename, string user = null, string password = null, string devicename = null) 41 { 42 if (devicename != string.Empty)//為空時在已有的列表中得不到信息,所以不用判斷了 43 { 44 //得到當前所有的映射驅動器及地址 45 var netUseList = GetAllDevic(); 46 47 //標准格式 48 sharename = @"\\" + sharename.Trim('\\'); 49 50 if (devicename == null)//自動分配驅動器號 51 { 52 for (int i = 0; i < driveNames.Length; i++) 53 { 54 string name = driveNames[i]; 55 if (netUseList.ContainsKey(name)) continue;//已存在 56 devicename = name; 57 break; 58 59 } 60 if (devicename == null) 61 throw new ArgumentException("當前沒有可用的驅動器號."); 62 } 63 else 64 { 65 //標准格式 66 devicename = devicename.Trim('\\', ':'); 67 68 //判斷是否已存在相同的共享 69 foreach (var kv in netUseList) 70 { 71 if (kv.Value == null) continue; 72 //驅動器號和共享路徑一致 73 if (string.Equals(kv.Key, devicename, StringComparison.CurrentCultureIgnoreCase) 74 && string.Equals(kv.Value, sharename, StringComparison.CurrentCultureIgnoreCase)) 75 return devicename;//已存在,返回 76 } 77 //共享沒在用,但其他占用驅動器號 78 if (netUseList.ContainsKey(devicename)) 79 throw new ArgumentException("驅動器號" + devicename + "正在使用."); 80 } 81 82 } 83 using (System.Diagnostics.Process myProcess = new System.Diagnostics.Process()) 84 { 85 string command = string.Format(@"use "); 86 if (devicename != string.Empty) 87 { 88 command += devicename.Trim('\\', ':') + ": "; 89 90 } 91 command += string.Format("\"{0}\" ", sharename); 92 if (user != null) 93 { 94 command += string.Format(@"""{0}"" /user:""{1}"" ", password, user); 95 } 96 System.IO.File.AppendAllText("d:\\log.txt", command); 97 //通過net use的命令 創建共享。 98 System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo("net ", command); 99 info.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; 100 info.CreateNoWindow = true; 101 info.UseShellExecute = false; 102 info.RedirectStandardError = true; 103 myProcess.StartInfo = info; 104 myProcess.Start(); 105 myProcess.WaitForExit(6000); 106 string errormsg = myProcess.StandardError.ReadToEnd(); 107 108 myProcess.Close(); 109 110 if (!string.IsNullOrEmpty(errormsg)) 111 throw new InvalidOperationException(errormsg); 112 } 113 return devicename; 114 } 115 116 /// <summary> 117 /// 獲取當前所有的驅動器 118 /// <para>Key:盤符</para> 119 /// <para>Value:盤符ProviderName(源路徑信息)。如果為本地磁盤則為null</para> 120 /// </summary> 121 /// <returns></returns> 122 private static Dictionary<string, string> GetAllDevic() 123 { 124 //得到當前所有的驅動器 125 var devicList = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); 126 var selectQuery = new System.Management.SelectQuery("select * from win32_logicaldisk"); 127 var searcher = new System.Management.ManagementObjectSearcher(selectQuery); 128 129 foreach (System.Management.ManagementObject disk in searcher.Get()) 130 { 131 var devName = disk["DeviceID"]; 132 if (devName == null) 133 devName = disk["Name"]; 134 var diskName = devName.ToString().Trim('\\', ':'); 135 devicList.Add(diskName, null); 136 var access = disk["Access"]; 137 if (access == null || access.ToString() != "0") continue;//不可訪問 138 var drivetype = disk["DriveType"]; 139 if (drivetype == null || drivetype.ToString() != "4") continue;//不是網絡驅動器 140 var providername = disk["ProviderName"]; 141 if (providername == null) continue;//沒有映射源 142 143 devicList[diskName] = providername.ToString(); 144 } 145 return devicList; 146 } 147 }
測試:多個客戶端同時操作未報錯。
另外,建議在對文件操作時進行異常捕捉判斷,如果捕捉到FileNotFoundException 和 DirectoryNotFoundException ,說明可能發生了映射不可用的情況,應當自動建立映射。
總結
從一開始接觸到這個BUG,到解決,斷斷續續經歷了一周左右。寫代碼的時間也就半天不到,更多的是在查找資料、各地“求救”,很無望的趕腳。。。好了,碎碎念結束。
其實仔細想想,冷靜下來,才會有更多的思路。
最后,如果各位有更好的方式?還望示下。