從 ASP.NET Core 應用程式存取其他伺服器上的共享資料夾(網路連接程式版本)

更新頁 :
頁面創建日期 :

操作驗證環境

Visual 工作室
  • Visual Studio 2022
ASP.NET 核心
  • 8 (Razor Pages, MVC)
Windows 伺服器
  • 2022 (ASP.NET 核心系統要求)
  • 2019(共用資料夾部署伺服器)
IIS的
  • 10.0

操作環境

我們尚未在所有情況下對其進行測試,但它應該在以下條件下正常工作:

Visual 工作室
  • 任何可以開發 ASP.NET 核心項目的東西
ASP.NET 核心
  • 任何版本(MVC、Razor Pages、API)
Windows 伺服器
  • Windows Server 2008 或更高版本
IIS的
  • 7.0 或更高版本

前提

  • ASP.NET Core 應用程式旨在在 IIS 上運行。
  • 由於它使用 Windows API 進行身份驗證,因此它不適用於非 Windows。

環境

在以下環境中驗證。

PC和伺服器 的使用目的
Windows 11(本地) 開發程序的環境。
SV2022測試 運行 IIS 和 ASP.NET Core 的環境。 從此處訪問 SV2019Test 共享資料夾
SV2019測試 具有共用資料夾的伺服器

此外,各種設置如下。

參數名稱
訪問使用者名 共用使用者
共用資料夾名稱 SharedFolder

構建共享資料夾伺服器

創建使用者

創建使用者以存取公共資料夾。 在這種情況下,我們將創建一個本地帳戶,但如果您正在處理域(如 Active Directory)中的伺服器和帳戶,您也可以使用它。

創建用戶的過程超出了這些提示的範圍,因此我們不會過多地介紹。

SharedUser在本例中,我們將使用名稱 創建它。 由於此使用者不操作螢幕或更改設置,因此無法更改密碼。

如果保留預設值,則可以使用遠端桌面等使用此用戶登錄,因此請從組 Users 中刪除。

創建共享資料夾

在哪裡創建它並不重要。 這是因為其他伺服器不關心物理資料夾的位置。 在這種情況下,我們將創建一個直接在 C 盤下 SharedFolder 命名的資料夾並共用它。

打開屬性並配置共享設置。

共用資料夾名稱應 SharedFolder 為 。 此名稱將對其他伺服器可見。 添加 SharedUser 許可權。

Everyone刪除現有的 .

使用“更改”許可權進行確認。

由於我們只添加了可以從外部訪問的許可權,因此我們將在內部 SharedUser 設置它,以便可以在此資料夾中運行。

使用“更改”許可權進行確認。

創建一個檔來檢查操作。 在此程式中,編碼是用UFT-8處理的,因此請將其保存為UTF-8。

如果可以從另一台 PC 存取資源管理員 \\<サーバー名>\SharedUser 使用 登錄並查看檔,則可以。

創建一個程式以從 ASP.NET Core 應用程式從共用資料夾讀取和寫入檔案

Mr./Ms. 操作是按鍵。

  • 將檔案載入到共享資料夾中並在螢幕上顯示它們
  • 將新檔寫入共享資料夾

過程。

Razor Pages 和MVC代碼是這方面的示例,但訪問共用資料夾的程式對於兩者是相同的。 Web API 也是如此。 如果有的話,它也應該在用戶端程式中工作。

在特定進程期間連接到網路的進程

在專案中的任意位置創建以下代碼。 類名是 SharedFolderAccessor ,但名稱是任意的。 SharedFolderAccessor 在創建 Dispose 的實例之前,您可以存取共享資料夾。 using 允許您指定可以在顯式範圍內訪問訪問的時間段。

using System.ComponentModel;
using System.Net;
using System.Runtime.InteropServices;

/// <summary>
/// 共有フォルダにユーザー名とパスワードでアクセスするためのクラスです。
/// using を使用すればそのスコープの間、共有フォルダにアクセスできます。
/// </summary>
public class SharedFolderAccessor : IDisposable
{
  private readonly string _networkName;

  /// <summary>
  /// コンストラクタです。
  /// </summary>
  /// <param name="networkName">共有フォルダのあるサーバーを「\\&lt;サーバー名&gt;」形式で指定します。</param>
  /// <param name="credentials">共有フォルダにアクセスするための資格情報です。</param>
  /// <exception cref="Win32Exception"></exception>
  public SharedFolderAccessor(string networkName, NetworkCredential credentials)
  {
    _networkName = networkName;

    // 接続するネットワークの情報を設定
    var netResource = new NetResource
    {
      Scope = ResourceScope.GlobalNetwork,
      ResourceType = ResourceType.Disk,
      DisplayType = ResourceDisplaytype.Share,
      RemoteName = networkName,
    };

    // ドメインがある場合はドメイン名も指定、ない場合はユーザー名のみ
    var userName = string.IsNullOrEmpty(credentials.Domain)
        ? credentials.UserName
        : $@"{credentials.Domain}\{credentials.UserName}";

    // 共有フォルダにユーザー名とパスワードで接続
    var result = WNetAddConnection2(netResource, credentials.Password, userName, 0);

    if (result != 0)
    {
      throw new Win32Exception(result, $"共有フォルダに接続できませんでした。(エラーコード:{result})");
    }

    // 正常に接続できれば WNetCancelConnection2 を呼び出すまではプログラムで共有フォルダにアクセス可能
  }

  ~SharedFolderAccessor()
  {
    // Dispose を呼び忘れたときの保険
    WNetCancelConnection2(_networkName, 0, true);
  }

  public void Dispose()
  {
    WNetCancelConnection2(_networkName, 0, true);
    GC.SuppressFinalize(this);  // Dispose を明示的に呼んだ場合はデストラクタの処理は不要
  }

  /// <summary>
  /// ネットワーク リソースへの接続を確立し、ローカル デバイスをネットワーク リソースにリダイレクトできます。
  /// </summary>
  /// <param name="netResource">ネットワーク リソース、ローカル デバイス、ネットワーク リソース プロバイダーに関する情報など。</param>
  /// <param name="password">ネットワーク接続の作成に使用するパスワード。</param>
  /// <param name="username">接続を確立するためのユーザー名。</param>
  /// <param name="flags">接続オプションのセット。</param>
  /// <returns></returns>
  [DllImport("mpr.dll")]
  private static extern int WNetAddConnection2(NetResource netResource, string password, string username, int flags);

  /// <summary>
  /// 既存のネットワーク接続を取り消します。
  /// </summary>
  /// <param name="name">リダイレクトされたローカル デバイスまたは切断するリモート ネットワーク リソースの名前。</param>
  /// <param name="flags">接続の種類。</param>
  /// <param name="force">接続に開いているファイルまたはジョブがある場合に切断を行う必要があるかどうか。</param>
  /// <returns></returns>
  [DllImport("mpr.dll")]
  private static extern int WNetCancelConnection2(string name, int flags, bool force);

  /// <summary>
  /// NETRESOURCE 構造体を定義しています。
  /// </summary>
  [StructLayout(LayoutKind.Sequential)]
  private class NetResource
  {
    public ResourceScope Scope;
    public ResourceType ResourceType;
    public ResourceDisplaytype DisplayType;
    public int Usage;
    public string LocalName = "";
    public string RemoteName = "";
    public string Comment = "";
    public string Provider = "";
  }

  /// <summary>
  /// ネットワークリソースのスコープ。
  /// </summary>
  private enum ResourceScope : int
  {
    /// <summary>ネットワークリソースへの現在の接続。</summary>
    Connected = 1,
    /// <summary>すべてのネットワークリソース。</summary>
    GlobalNetwork = 2,
    Remembered = 3,
    Recent = 4,
    /// <summary>ユーザーの現在および既定のネットワークコンテキストに関連付けられているネットワークリソース。</summary>
    Context = 5,
  };

  /// <summary>
  /// リソースの種類。
  /// </summary>
  private enum ResourceType : int
  {
    /// <summary>印刷リソースとディスクリソースの両方のコンテナー、または印刷またはディスク以外のリソースなど。</summary>
    Any = 0,
    /// <summary>共有ディスクボリューム。</summary>
    Disk = 1,
    /// <summary>共有プリンター。</summary>
    Print = 2,
    Reserved = 8,
  }

  /// <summary>
  /// ユーザーインターフェイスで使用する必要がある表示の種類。
  /// </summary>
  private enum ResourceDisplaytype : int
  {
    /// <summary>リソースの種類を指定しないネットワークプロバイダーによって使用されます。</summary>
    Generic = 0x0,
    /// <summary>サーバーのコレクション。</summary>
    Domain = 0x01,
    /// <summary>サーバー。</summary>
    Server = 0x02,
    /// <summary>共有ポイント。</summary>
    Share = 0x03,
    File = 0x04,
    Group = 0x05,
    /// <summary>ネットワークプロバイダー。</summary>
    Network = 0x06,
    Root = 0x07,
    Shareadmin = 0x08,
    /// <summary>ディレクトリ。</summary>
    Directory = 0x09,
    Tree = 0x0a,
    Ndscontainer = 0x0b,
  }
}

由於它使用 Win32 API 和 WNetAddConnection2 WNetCancelConnection2 ,因此它僅在 Windows 環境中工作。 我暫時有評論,但如果您想瞭解更多,請在互聯網上查找。 除了共享資料夾外,您還可以執行訪問網路資源的操作。

它易於使用,如果您編寫以下內容,則 using 可以在作用域期間訪問共享資料夾。

using (new SharedFolderAccessor($@"\\{serverName}", credentials))
{
  // この間は共有フォルダにアクセスできる
}

但是,實際上 WNetCancelConnection2 ,在調用時不會立即斷開連接,因此 using 即使在作用域之後也可以訪問共享資料夾。

使用 SharedFolderAccessor 測試代碼

由於訪問共享資料夾的過程不依賴於框架,因此我們創建了一種用於讀取和寫入檔的通用測試方法。 Pazor Pages 和MVC應該被稱為相同的。

內容是一個進程,該過程將傳遞給參數的文本寫入共用資料夾,讀取共用資料夾中已有的文字檔,並返回文本。

using System.Net;

public static class Util
{
  public static string ReadAndWrite(string text)
  {
    var serverName = "ServerName";
    var folderName = "SharedFolder";
    var inputFileName = "Input.txt";
    var outputFileName = "Output.txt";
    var username = "SharedUser";
    var password = "password";

    var credentials = new NetworkCredential(username, password);
    using (new SharedFolderAccessor($@"\\{serverName}", credentials))
    {
      // ファイルの書き出し
      System.IO.File.WriteAllText(Path.Combine($@"\\{serverName}\{folderName}", outputFileName), text);

      // ファイルの読み込み
      return System.IO.File.ReadAllText(Path.Combine($@"\\{serverName}\{folderName}", inputFileName));
    }
  }
}

Razor 頁面

在Razor Pages中,我們在Index.cshtml 中放置一個按鈕,按兩下它,運行測試代碼,然後在螢幕上顯示結果。 在某些情況下,共用資料夾可能無法訪問,因此將其包含在 try-catch 中。

Index.cshtml.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace SharedFolderAccessRazorPages.Pages
{
  public class IndexModel : PageModel
  {
    private readonly ILogger<IndexModel> _logger;
    public string Message { get; set; } = "";

    public IndexModel(ILogger<IndexModel> logger)
    {
      _logger = logger;
    }

    public void OnPost()
    {
      try
      {
        Message = Util.ReadAndWrite($"プログラムからの書き込み ({DateTime.Now})");
      }
      catch (Exception ex)
      {
        Message = ex.ToString();
      }
    }
  }
}

索引.cshtml

@page
@model IndexModel
@{
  ViewData["Title"] = "Home page";
}

<div class="text-center">
  <h1 class="display-4">Welcome</h1>
  <p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

@* ここから追加 *@
<form method="post">
  <button type="submit">サブミット</button>
</form>

<div>
  @Model.Message
</div>
@* ここまで追加 *@

MVC的

同樣,在MVC中, Index.cshtml 將一個按鈕放在 中,按兩下時,它會調用共用資料夾的測試代碼。

控制器/HomeController.cs

// 省略

namespace SharedFolderAccessMvc.Controllers
{
  public class HomeController : Controller
  {
    // 省略

    public IActionResult Index()
    {
      return View();
    }

    // ここから追加
    [HttpPost]
    public IActionResult Index(string dummy)
    {
      try
      {
        ViewData["Message"] = Util.ReadAndWrite($"プログラムからの書き込み ({DateTime.Now})");
      }
      catch (Exception ex)
      {
        ViewData["Message"] = ex.ToString();
      }
      return View();
    }
    // ここまで追加

    public IActionResult Privacy()
    {
      return View();
    }

    // 省略
  }
}

索引.cshtml

@{
  ViewData["Title"] = "Home Page";
}

<div class="text-center">
  <h1 class="display-4">Welcome</h1>
  <p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

@* ここから追加 *@
<form method="post">
  <button type="submit">サブミット</button>
</form>

<div>
  @ViewData["Message"]
</div>
@* ここまで追加 *@

操作確認

調試並驗證是否可以成功訪問共享資料夾。

構建應用程式伺服器

由於我們已經確認可以通過運行程式訪問共享資料夾,因此無需進行下一步。 如果要檢查IIS伺服器上的操作,可以按照以下步驟進行操作。

從這裡開始,它是一個補充,所以我不會解釋它太詳細,而是主要解釋圖像。

安裝 IIS

默認情況下,從伺服器管理員安裝它。 我不會詳細介紹該過程。

不需要其他功能。

目前不需要其他 IIS 服務。

ASP.NET Core Runtime Hosting Bundle 安裝

由於我們使用的是 Core 8 ASP.NET 因此我們需要相應地安裝運行時。 從以下 URL 下載:

為了在IIS中運行 ASP.NET Core,您需要稱為“Hosting Bundle”的東西。 從 ASP.NET Core 運行時下載“Hosting Bundle”。

下載后,在伺服器上運行它。

按照嚮導進行安裝。

發佈程式

將要部署到 IIS 的程式作為檔從 Visual Studio 輸出。

針對 Windows 修改它。

完成後發佈。

如果按下目標位置,則可以打開已發佈檔所在的資料夾。

你不必把它們都帶上,但如果你不確定,你可以暫時把它們都帶上。 此時,您需要做的就是確保檔存在。

創建和部署 Web 應用程式

在 Windows 管理工具中,打開 Internet Information Services (IIS) 管理器。

我們將創建一個網站,但這次我們將使用從一開始就存在的“默認網站”。

選擇「預設網站」后,按兩下「資源管理器」打開資料夾。 在此處複製已發佈的程式。 您可以刪除原始檔案。

從 IIS 連結打開頁面,查看螢幕是否顯示。 您可以先開啟 Web 瀏覽器並直接輸入 URL。

操作確認

按兩下該按鈕以驗證其是否正常工作。 在上面,它是從Web伺服器內部訪問的,但是由於它是Web伺服器,因此應該可以從其他PC操作它。