也許很多朋友在做WEB項目的時候都會碰到這樣一個需求:
當用戶上傳文件時,需要將上傳的文件保存到另外一臺專門的文件服務器。
要實現這樣一個功能,有兩種解決方案:
方案一、在文件服務器上新建一站點,用來接收上傳的文件,然後保存。
方案二、將文件服務器的指定目錄共享給WEB服務器,用來保存文件。
方案一不用多說,應該是很簡單的了,將上傳文件的FORM表單的ACTION屬性指向文件服務器上的站點即可,我們來重點說下方案二。
也許你會說,其實方案二也很簡單,在WEB服務器上做下磁盤映射,然後直接訪問不就行了。其實不是這樣的,IIS默認賬戶爲NETWORK_SERVICE,該賬戶是沒權限訪問共享目錄的,所以當你把站點部署到IIS上的時候,再訪問映射磁盤就會報“找不到路徑”的錯誤。所以,直接創建磁盤映射是行不通的,我們需要在程序中用指定賬戶創建映射,並用該賬戶運行IIS進程,下面來說下詳細步驟及相關代碼。
1、在文件服務器上創建共享目錄,並新建訪問賬戶。
比如共享目錄爲:\\192.168.0.9\share
訪問賬戶爲:user-1 密碼爲:123456
2、在WEB服務器上新建用戶:user-1 密碼爲:123456,用戶組選擇默認的user組即可。
3、在WEB項目中新建公共類WNetHelper
using System.Runtime.InteropServices;
public class WNetHelper
{
[DllImport("mpr.dll", EntryPoint = "WNetAddConnection2")]
private static extern uint WNetAddConnection2(NetResource lpNetResource, string lpPassword, string lpUsername, uint dwFlags);
[DllImport("Mpr.dll", EntryPoint = "WNetCancelConnection2")]
private static extern uint WNetCancelConnection2(string lpName, uint dwFlags, bool fForce);
[StructLayout(LayoutKind.Sequential)]
public class NetResource
{
public int dwScope;
public int dwType;
public int dwDisplayType;
public int dwUsage;
public string lpLocalName;
public string lpRemoteName;
public string lpComment;
public string lpProvider;
}
/// <summary>
/// 爲網絡共享做本地映射
/// </summary>
/// <param name="username">訪問用戶名(windows系統需要加計算機名,如:comp-1\user-1)</param>
/// <param name="password">訪問用戶密碼</param>
/// <param name="remoteName">網絡共享路徑(如:\\192.168.0.9\share)</param>
/// <param name="localName">本地映射盤符</param>
/// <returns></returns>
public static uint WNetAddConnection(string username, string password, string remoteName, string localName)
{
NetResource netResource = new NetResource();
netResource.dwScope = 2;
netResource.dwType = 1;
netResource.dwDisplayType = 3;
netResource.dwUsage = 1;
netResource.lpLocalName = localName;
netResource.lpRemoteName = remoteName.TrimEnd('\\');
uint result = WNetAddConnection2(netResource, password, username, 0);
return result;
}
public static uint WNetCancelConnection(string name, uint flags, bool force)
{
uint nret = WNetCancelConnection2(name, flags, force);
return nret;
}
}
4、爲IIS指定運行賬戶user-1
要實現此功能,有兩種辦法:
a) 在web.config文件中的<system.web>節點下,添加如下配置:<identity impersonate="true" userName="user-1" password="123456" />
b) 在WEB項目中添加公用類LogonImpersonate
public class LogonImpersonate : IDisposable
{
static public string DefaultDomain
{
get
{
return ".";
}
}
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_PROVIDER_DEFAULT = 0;
[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
extern static int FormatMessage(int flag, ref IntPtr source, int msgid, int langid, ref string buf, int size, ref IntPtr args);
[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
extern static bool CloseHandle(IntPtr handle);
[System.Runtime.InteropServices.DllImport("Advapi32.dll", SetLastError = true)]
extern static bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken
);
IntPtr token;
System.Security.Principal.WindowsImpersonationContext context;
public LogonImpersonate(string username, string password)
{
if (username.IndexOf("\\") == -1)
{
Init(username, password, DefaultDomain);
}
else
{
string[] pair = username.Split(new char[] { '\\' }, 2);
Init(pair[1], password, pair[0]);
}
}
public LogonImpersonate(string username, string password, string domain)
{
Init(username, password, domain);
}
void Init(string username, string password, string domain)
{
if (LogonUser(username, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token))
{
bool error = true;
try
{
context = System.Security.Principal.WindowsIdentity.Impersonate(token);
error = false;
}
finally
{
if (error)
CloseHandle(token);
}
}
else
{
int err = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
IntPtr tempptr = IntPtr.Zero;
string msg = null;
FormatMessage(0x1300, ref tempptr, err, 0, ref msg, 255, ref tempptr);
throw (new Exception(msg));
}
}
~LogonImpersonate()
{
Dispose();
}
public void Dispose()
{
if (context != null)
{
try
{
context.Undo();
}
finally
{
CloseHandle(token);
context = null;
}
}
}
}
在訪問映射磁盤之前首先調用此類爲IIS更換運行用戶:LogonImpersonate imper = new LogonImpersonate("user-1", "123456");
5、在訪問共享目錄前,調用WNetHelper.WNetAddConnection,添加磁盤映射
public static bool CreateDirectory(string path)
{
uint state = 0;
if (!Directory.Exists("Z:"))
{
state = WNetHelper.WNetAddConnection(@"comp-1\user-1", "123456", @"\\192.168.0.9\share", "Z:");
}
if (state.Equals(0))
{
Directory.CreateDirectory(path);
return true;
}
else
{
throw new Exception("添加網絡驅動器錯誤,錯誤號:" + state.ToString());
}
}
6、完成。
簡潔代碼就是:
LogonImpersonate imper = new LogonImpersonate("user-1", "123456");
WNetHelper.WNetAddConnection(@"comp-1\user-1", "123456", @"\\192.168.0.9\share", "Z:");
Directory.CreateDirectory(@"Z:\newfolder");
file1.SaveAs(@"Z:\newfolder\test.jpg");
有不明白的可以發EMAIL給我:[email protected]